Sql 如何避免大in子句?

Sql 如何避免大in子句?,sql,sql-server,optimization,Sql,Sql Server,Optimization,我有三张桌子: table_product (30 000 row) --------- ID label _ 及 所以我需要从所有这些表中加载数据,所以这里是我要做的: 1/像这样从表_产品加载数据 select * from table_product where label like 'gun%' select * from table_period where FK_ID_product IN(list of all the ids selected in the 1) selec

我有三张桌子:

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

所以我需要从所有这些表中加载数据,所以这里是我要做的: 1/像这样从表_产品加载数据

select *
from table_product
where label like 'gun%'
select *
from table_period
where FK_ID_product IN(list of all the ids selected in the 1)
select *
from table_special_offer
where FK_ID_period IN(list of all the ids selected in the 2)
2/按如下方式从表中加载数据

select *
from table_product
where label like 'gun%'
select *
from table_period
where FK_ID_product IN(list of all the ids selected in the 1)
select *
from table_special_offer
where FK_ID_period IN(list of all the ids selected in the 2)
3/从表\u特殊\u中加载数据,如下所示

select *
from table_product
where label like 'gun%'
select *
from table_period
where FK_ID_product IN(list of all the ids selected in the 1)
select *
from table_special_offer
where FK_ID_period IN(list of all the ids selected in the 2)
正如您可能认为第3点中的IN子句可能非常非常大,比如75000大,因此我很有可能得到超时或达到expression services限制

你有过这样的经历吗?你是如何避免的

附言: 上下文:SQL server 2005、.net 2.0 请不要告诉我我的设计不好,或者我不应该选择*,我只是简化了我的问题,所以它比500页描述我的业务要简单一点


谢谢。

不要在in子句中使用显式的值列表。相反,将查询编写为

... FK_ID_product IN (select ID
from table_product
where label like 'gun%')
A给你同样的结果

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%'
切换到使用联接:

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与JOIN与EXISTS的区别-

首先是一些代码

使用联接:

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个表的所有数据方面绝对是有效的。如果您只需要使用ID的from table_product和table_period,那么使用嵌套IN语句可以很好地适应索引表上的搜索条件,如果使用的条件是整数(如我假设您的FK_ID_product是整数),则使用IN也可以

需要记住的一件重要事情是,每个数据库和关系表的设置都会有不同的行为,在一个数据库到另一个数据库中不会有相同的优化结果。尝试手头上所有的可能性,使用最适合你的。当您需要检查性能时,查询分析器在这种情况下非常有用

当我们试图通过ID连接和基于链接表的条件将客户帐户连接到相应的地址时,我遇到了这种情况。我们有另一个表,其中显示了使用某些设备的客户,我们必须对这些设备进行字符串搜索。奇怪的是,在一个查询中使用这两种方法更快:

-使用IN子句将WHERE Desc类似“%Equipment%”的查询连接到客户机表,然后将其连接到addresses表:

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的文章链接,这绝对是一个很好的资源。

我很想知道这是否会有所改进:

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
)

。。。只是一个建议…

中终于有了我的答案:表变量有点像@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,我使用类sqldatareader的ExecuteReader、Read和NextResult

我得到了我想要的一切: -我的数据 -与join解决方案不同,我没有太多的数据 -我不会对子查询执行两次相同的查询式解决方案
-我不必更改映射代码1row=1业务对象

您的解决方案的问题我将获得每个期间行和每个特价笛卡尔产品的产品信息。因此,如果一个产品的信息非常大,我将有30000*225000*10000=675*10^11行,而不是30000+225000+10000=265*10^3行,那么比例是3*10^7。也许我错了……我错了,在你的例子中是225000行,但在每一行中我们都会得到产品信息+期限+可能是特殊报价,所以你认为如果有很多产品信息,它会导致一些性能问题吗?这将是一个解决方案,可能使用exists而不是se@atathedev链接。但这里的问题是,如果我有很多产品/期间,此解决方案将是有效的,如果我只选择少数,我最好执行显式in。很抱歉,您仍然有一个大in子句,并且可能会得到一个表达式服务限制,因为已达到错误。这就是为什么我们有联接并存在。此外,除非列名相同,否则它将尝试比较表_product与FK_ID_product表_period中的整行。您不会匹配任何记录,因为您没有在标准中为FK_ID_产品提供要匹配的一列数据。这应该是表中的select ID_product not select*@remi:duplicate?它不再显示了,但我已经在AdaTheDev发布前一分钟发布了。似乎我们都有相同的想法。这就是我所说的复制,你有sme的想法,几乎相同的代码,所以感谢你给出了答案,但你已经3岁了,有相同的解决方案:也许是解决方案:。说真的,它可能有性能问题,但它永远不会像使用in子句那样接近
条款胜出。。。如果in子句中有60000项,您的解决方案可能会获胜。你的解决方案的问题是它使映射代码更加复杂是的,我知道NH,但在我的解决方案中效率较低1行=1对象。你知道NH吗?我不。什么是NH?