簡體   English   中英

根據C#列表而不是過濾器表過濾SQL

[英]Filter sql based on C# List instead of a filter table

假設我有一個包含以下數據的表:

在此處輸入圖片說明

現在,我想按主鍵部門和號碼進行篩選。 我列出了必須在代碼中過濾的部門和數字組合。 在我看來,我將創建一個導致以下結果的聯接:

select * from employee e
inner join dynamicTable dyn on e.Department = dyn.Department 
                           and e.Number = dyn.Number;

dynamicTable是我的C#代碼List ,具有要過濾的主鍵,但是我不知道如何將該列表傳遞到數據庫級別。

我不想從我的雇員表中加載所有內容,也不希望通過linq或其他方式在代碼中進行過濾,因為我的數據庫中有數百萬個雇員。

我已經考慮過要結合primary_keys並where in (...)創建一個where in (...) ,但是firebird最多只能在where in限制1500條記錄。

使用的數據庫是Firebird 2.1版

我個人可以看到兩個技巧。 還有“過去的沖擊”。

路線1。 使用GTT:GLOBAL TEMPORARY TABLE

GTT是在FB 2.1中引入的(並且您可以使用它),可以按連接或按事務進行操作。 您可能需要每筆交易。 這種差異是關於數據(行),架構(結構和索引,元數據)是持久的。 請參閱GTT文檔中的ON COMMIT DELETE ROWS選項。

等等。

這樣,您可以打開事務,用列表中的數據填充GTT(將這1500個值對從工作站復制到服務器中),在該GTT上運行查詢JOINing,然后COMMIT事務和表內容被自動刪除。

如果您可以在會話中運行許多幾乎相似的查詢,則可以改用GTT每次連接並根據需要修改數據,而不是為每個下一個事務中的每個下一個查詢重新填充它,但這是一種更復雜的方法。 在每個COMMIT的盡早COMMIT是我更喜歡的默認方法,直到爭論為什么在這種特定情況下每次連接會更好。 只是不要在查詢之間在服務器上保留這些垃圾。

路線2。 使用字符串搜索-反向的LIKE匹配。

該方法以其基本形式用於搜索一些龐大且任意的整數列表。 您的情況要復雜一些,您要匹配成對的數字,而不是單數。

簡單的想法就是這樣,假設我們要獲取ID列可以為1、4、12、24的行。簡單的方法是對每個值進行4個查詢,或者使WHERE ID = 1 or ID = 4 or ...或使用WHERE id IN (1,4,12,24) 在內部, IN將展開為= or = or = ,然后很可能作為四個查詢執行。 對於長列表來說不是很有效。

因此,為了匹配很長的列表,我們可以形成一個特殊的字符串。 並將其作為文本進行匹配。 這樣會使匹配本身的效率大大降低,並禁止使用任何索引,服務器會在整個表上運行NATURAL SCAN,但是會進行一次掃描。 當匹配列表很大時,單次全表掃描比數千個按索引讀取要高效。 但是-僅當列表與表的比例很大時,才取決於您的特定數據。

我們使文本列出所有目標值,並用AND穿插在定界符“〜1〜4〜12〜24〜”中。 現在,我們在ID列中使用相同的delimiter-number-delimiter字符串,並查看是否可以找到這樣的子字符串。

LIKE / CONTAINING的通常用法是將列與以下數據進行匹配: SELECT * from the_table WHERE column_name CONTAINING value_param
我們將其反向, SELECT * from the_table WHERE value_param CONTAINING column_name-based-expression

  SELECT * from the_table WHERE '~1~4~12~24~' CONTAINING '~' || ID || '~' 

假設ID將自動從整數轉換為字符串。 如果不是,則必須手動進行: .... CONTAINING '~' || CAST( ID as VARCHAR(100) ) || '~' .... CONTAINING '~' || CAST( ID as VARCHAR(100) ) || '~'

您的情況有點復雜,您需要匹配兩個數字,即Department和Number,因此,如果遵循這種方式,則必須使用兩個不同的定界符。 就像是

SELECT * FROM employee e WHERE
  '~1@10~1@11~2@20~3@7~3@66~' CONTAINING
  '~' || e.Department || '@' || e.Number || '~'

陷阱:您說目標清單是1500個元素。 目標行將是...長。 多長時間???

Firebird中的VARCHAR受32KB AFAIR限制,並且應將較長的文本作為文本BLOB進行制作,但功能會有所減少。 LIKE是否可以對FB2.1中的BLOB起作用? 我不記得了,請查看發行說明。 還要檢查您的庫是否甚至允許您將參數類型指定為BLOB而不是字符串。 現在,您的連接字符是什么? 如果是Windows-1250或Windows-1251之類的字符,那么一個字符就是一個字節,那么您可以將32K字符放入32KB。 但是,如果您的應用程序設置的CONNECTION CHARSET是UTF-8,則每個字母占用4個字節,最大VARCHARable字符串將減少為8K個字母。

您可以嘗試避免對此長字符串使用參數,並將目標字符串常量內聯到SQL語句中。 但是,您可能會達到最大SQL語句長度的限制。

另請參見: c:\\ Program Files \\ Firebird \\ Firebird_2_1 \\ doc \\ README.monitoring_tables.txt中的 MON$CHARACTER_SET_ID ,然后是FB docs中的SYSTEM TABLES部分,如何將ID映射到字符集文本名稱。

3號路線可憐的人的GTT。 輸入偽表。

在引入GTT之前,有時可以在較早的IB / FB版本中使用此技巧。

優點:您不需要更改永久性的SCHEMA。
缺點:如果不更改SCHEME,則不能創建索引,也不能使用索引連接。 同樣,您可以達到單個SQL語句的長度限制。

確實,不要認為這適用於您的情況,只是為了使答案完整,我想也應該提到這個技巧。

select * from employee e, (
  SELECT 1 as Department, 10 as Number FROM RDB$DATABASE
  UNION ALL SELECT 1, 11 FROM RDB$DATABASE
  UNION ALL SELECT 2, 20 FROM RDB$DATABASE
  UNION ALL SELECT 3, 7 FROM RDB$DATABASE
  UNION ALL SELECT 3, 66 FROM RDB$DATABASE
) t, 
where e.Department = t.Department 
  and e.Number = t.Number

粗魯和丑陋,但有時此偽表可能會有所幫助。 什么時候? 在大多數情況下,它有助於從不需要的索引中進行批量INSERT-from-SELECT的創建:-D它很少適用於SELECTs-但要知道竅門。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM