如何避免大条款?

时间:2010-11-23 10:08:37

标签: sql sql-server optimization

我有3张桌子:

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

_

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

table_special_offer (10 000 row)
-----
ID
label
date_start,
date_end,
special_offer_price
FK_ID_period

所以我需要从所有这些表中加载数据,所以这就是我的工作: 1 /从“table_product”加载数据

select *
from table_product
where label like 'gun%'

2 /从“table_period”加载数据

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

3 /从“table_special_offer”加载数据

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

你可能认为第3点的IN子句可能非常大(比如75 000大),所以我有很多机会得到超时或类似“已达到表达式服务限制”

你有没有这样的事情,你是如何避免它的?

PS: 上下文:SQL server 2005,.net 2.0 (请不要告诉我我的设计不好,或者我不应该“选择*”,我只是简化了我的问题,所以它比描述我公司的500页更简单。)

感谢。

7 个答案:

答案 0 :(得分: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与JOIN与EXISTS的区别 - great article here.

答案 1 :(得分:1)

请勿在{{1​​}}子句中使用显式值列表。相反,写下你的查询

IN

答案 2 :(得分: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#我使用类sqldatareader的ExecuteReader,Read和NextResult

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

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

答案 3 :(得分:0)

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 :(得分: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%'" 

答案 5 :(得分: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%'" 

使用IN:

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”语句可以很好地调整索引表上的搜索条件(如果使用的标准是,则使用IN可以正常像我这样的整数假设你的FK_ID_product是。)

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

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

- 使用WH子描述'%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的文章链接,绝对是一个很好的资源。

答案 6 :(得分: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
)

......只是一个建议......