2010-11-23 81 views
2

我有3個表:如何避免大in子句?

table_product (30 000 row) 
--------- 
ID 
label 

_

table_period (225 000 row) 
--------- 
ID 
date_start 
date_end 
default_price 
FK_ID_product 

​​

所以我需要從這些表中加載數據,所以這裏是我做的: 1 /來自「table_product」的加載數據像這樣

從 「table_period」 像3210

2 /加載數據這

select * 
from table_period 
where FK_ID_product IN(list of all the ids selected in the 1) 
從 「table_special_offer」

3 /加載數據這樣

select * 
from table_special_offer 
where FK_ID_period IN(list of all the ids selected in the 2) 

你可能想IN子句中的點3可以非常非常大(比如75000個大),所以我有很多機會獲得暫停或類似「已達到表達式服務限制」。

你有沒有這樣的事情,你是如何設法避免它?

PS: 背景:SQL Server 2005中,.NET 2.0 (請不要告訴我,我的設計是壞的,或者我不應該做「SELECT *」,我只是簡化了我的問題,所以它是一個比描述我的業務的500頁簡單一點)。

謝謝。

回答

1

在終於有我的回答:表變量(有點像@ smirkingman的解決方案,但不與CTE),所以:

declare @product(id int primary key,label nvarchar(max)) 
declare @period(id int primary key,date_start datetime,date_end datetime,defaultprice real) 
declare @special_offer(id int,date_start datetime,date_end datetime,special_offer_price real) 

insert into @product 
select * 
from table_product 
where label like 'gun%' 

insert into @period 
select * 
from table_period 
where exists(
select * from @product p where p.id = table_period.FK_id_product 
) 

insert into @special_offer 
select * 
from table_special_offer 
where exists(
select * from @period p where p.id = table_special_offer.fk_id_period 
) 

select * from @product 
select * from @period 
select * from @special_offer 

這是SQL,並用C#我用的ExecuteReader,讀取和NextResult類的SqlDataReader的

http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqldatareader.aspx

我得到了我想要的: - 我DATAS - 我沒有太多的數據(不同於與解決方案加入) - 我不執行兩次相同的查詢(如與解決方案子查詢) - 我不必更改我的映射代碼(1行= 1業務對象)

1

請勿在IN子句中使用顯式值列表。請寫下您的查詢,如

... FK_ID_product IN (select ID 
from table_product 
where label like 'gun%') 
+1

不會真的有所幫助。 – gbn 2010-11-23 10:24:05

+0

這將是一個解決方案,也許存在而不是在(se @atathedev鏈接)。但這裏的問題是這個解決方案會很有效率,如果我有很多產品/期間,如果我只選擇幾個,我最好做一個明確的。 – 2010-11-23 10:25:40

+0

@gbn謹慎解釋? – 2010-11-23 10:25:44

0

A JOIN給出了相同的結果。

SELECT so.Col1 
     , so.Col2 
FROM table_product pt 
     INNER JOIN table_period pd ON pd.FK_ID_product = pt.ID_product 
     INNER JOIN table_special_offer so ON so.FK_ID_Period = pd.ID_Period 
WHERE pt.lable LIKE 'gun%' 
4

切換到使用聯接:

SELECT <FieldList> 
FROM Table_Product prod 
    JOIN Table_Period per ON prod.Id = per.FK_ID_Product 
    JOIN Table_Special_Offer spec ON per.ID = spec.FK_ID_Period 
WHERE prod.label LIKE 'gun%' 

有些事情,你應該知道的是IN的差異VS JOIN VS EXISTS - great article here.

0
SELECT * 
    FROM 
     table_product tp 
     INNER JOIN table_period tper 
      ON tp.ID = tper.FK_ID_product 
     INNER JOIN table_special_offer so 
      ON tper.ID = so.FK_ID_period 
    WHERE 
     tp.label like 'gun%'" 
0

首先是一些代碼...

使用JOIN:

SELECT 
    table_product.* --'Explicit table calls just for organisation sake' 
, table_period.* 
, table_special_offer.* 
    FROM 
     table_product 
     INNER JOIN table_period 
      ON table_product.ID = table_period.FK_ID_product 
     INNER JOIN table_special_offer 
      ON table_period.ID = table_special_offer.FK_ID_period 
    WHERE 
     tp.label like 'gun%'" 

使用:

SELECT 
    * 
FROM 
    table_special_offer 
WHERE FK_ID_period IN 
    (
    SELECT 
     FK_ID_period 
    FROM 
     table_period 
    WHERE FK_ID_product IN 
     (
     SELECT 
      FK_ID_product 
     FROM 
      table_product 
     WHERE label like '%gun' 
     ) AS ProductSub 
    ) AS PeriodSub 

根據你的表是如何被很好的索引都可以使用。如其他人所建議的內部聯接在執行查詢和返回3個表的所有數據時非常高效。如果您只需要使用table_producttable_period中的ID,那麼使用嵌套的「IN」語句可以很好地適應索引表上的搜索條件(如果使用的條件是整數,如我假設您的FK_ID_product是,則使用IN即可) 。

一個重要的事情要記住的是,每個數據庫和關係表設置將採取不同的行爲,您不會在一個數據庫中具有相同的優化結果。嘗試所有可能的手段,並使用最適合你的那個。當你需要檢查性能時,查詢分析器在這些時候非常有用。

當我們嘗試通過ID連接和基於鏈接表的條件將客戶帳戶連接到相應的地址時,我遇到了這種情況(我們有另一個表向客戶顯示了某些設備,我們必須執行字符串搜索)奇怪的是,我們在一個查詢中使用這兩種方法的速度更快:

- WHERE Desc LIKE'%Equipment%'的查詢使用IN子句「連接」到客戶端表,然後將其加入到地址表中:

SELECT 
    Address.* 
, Customers_Filtered.* 
FROM 
    Address AS Address 
INNER JOIN 
    (SELECT Customers.* FROM Customers WHERE ID IN (SELECT CustomerID FROM Equipment WHERE Desc LIKE '%Equipment search here%') AS Equipment) AS Customers_Filtered 
ON Address.CustomerID = Customers_Filtered.ID 

此風格的查詢(我道歉如果我的語法是不完全正確)在總體查詢變得更加複雜後最終變得更有效並且更容易組織。

希望這有助於 - 遵循@AdaTheDev的文章鏈接,絕對是一個很好的資源。

0

我很想知道這可能會使改進:

WITH products(prdid) AS (
    SELECT 
     ID 
    FROM 
     table_product 
    WHERE 
     label like 'gun%' 
), 
periods(perid) AS (
    SELECT 
     ID 
    FROM 
     table_period 
     INNER JOIN products 
      ON id = prdid 
), 
offers(offid) AS (
    SELECT 
     ID 
    FROM  
     table_special_offer 
     INNER JOIN periods 
      ON id = perid 
) 

...只是一個建議...