2011-04-05 85 views
0

时,我有一个查询,或者批量查询,那我就不能得到发挥好:)SQL Server性能问题加入

SELECT 
     C.ID AS cust_id, 
     C.State AS cust_state, 
     C.PrevState AS cust_previous_state, 
     C.ProjectID AS cust_imported_on_project_id, 
     C.CampaignID AS cust_imported_on_campaign_id, 
     C.Priority AS cust_priority, 
     C.Name AS cust_firstname, 
     C.Name2 AS cust_lastname, 
     C.AllocatedUser AS cust_allocated_user, 
     C.ED1 AS cust_social_security_number, 
     C.ED2 AS cust_customer_number, 
     C.ED3 AS cust_type, 
     C.ED4 AS cust_initial_fact_1, 
     C.ED5 AS cust_initial_fact_2, 
     C.ED6 AS cust_initial_fact_3, 
     C.ED7 AS cust_initial_fact_4, 
     C.ED8 AS cust_initial_fact_5, 
     C.ED9 AS cust_extra_1, 
     C.ED10 AS cust_extra_2, 
     CED2.ED11 AS cust_extra_3, 
     CED2.ED12 AS cust_extra_4, 
     CED2.ED13 AS cust_extra_5, 
     A.Serial AS address_serial, 
     A.PostAddress AS address_postal_address, 
     A.PostCode AS address_postal_code, 
     A.PostOffice AS address_city, 
     A.PhoneNr AS address_phonenumber, 
     A.FaxNr AS address_faxnumber, 
     A.EMail AS address_email, 
     A.Notes AS address_notes, 
     A.ED1 AS address_secondary_phonenumber, 
     A.ED2 AS address_origin_file, 
     A.State AS address_state 
FROM TCustomers C WITH (NOLOCK) 
    LEFT JOIN TCustED2 CED2 WITH (NOLOCK) ON C.ID = CED2.CustomerID 
    LEFT JOIN TAddresses A WITH (NOLOCK) ON C.AddressNr = A.Serial AND C.ID = A.CustomerID 
WHERE C.CampaignID IN(SELECT items FROM dbo.Split('196,195,210,206,205,207,204,200,209,213,197,198,214', ',')) AND C.InsDate > '2011-04-03 00:00:00' 

该查询本身是非常快和执行上万+行几秒钟。 问题是,我想使用订单表中的信息来过滤我的结果。

我曾尝试加入Orders表

SELECT 
     C.ID AS cust_id, 
     C.State AS cust_state, 
     C.PrevState AS cust_previous_state, 
     C.ProjectID AS cust_imported_on_project_id, 
     C.CampaignID AS cust_imported_on_campaign_id, 
     C.Priority AS cust_priority, 
     C.Name AS cust_firstname, 
     C.Name2 AS cust_lastname, 
     C.AllocatedUser AS cust_allocated_user, 
     C.ED1 AS cust_social_security_number, 
     C.ED2 AS cust_customer_number, 
     C.ED3 AS cust_type, 
     C.ED4 AS cust_initial_fact_1, 
     C.ED5 AS cust_initial_fact_2, 
     C.ED6 AS cust_initial_fact_3, 
     C.ED7 AS cust_initial_fact_4, 
     C.ED8 AS cust_initial_fact_5, 
     C.ED9 AS cust_extra_1, 
     C.ED10 AS cust_extra_2, 
     CED2.ED11 AS cust_extra_3, 
     CED2.ED12 AS cust_extra_4, 
     CED2.ED13 AS cust_extra_5, 
     A.Serial AS address_serial, 
     A.PostAddress AS address_postal_address, 
     A.PostCode AS address_postal_code, 
     A.PostOffice AS address_city, 
     A.PhoneNr AS address_phonenumber, 
     A.FaxNr AS address_faxnumber, 
     A.EMail AS address_email, 
     A.Notes AS address_notes, 
     A.ED1 AS address_secondary_phonenumber, 
     A.ED2 AS address_origin_file, 
     A.State AS address_state 
FROM TCustomers C WITH (NOLOCK) 
    LEFT JOIN TCustED2 CED2 WITH (NOLOCK) ON C.ID = CED2.CustomerID 
    LEFT JOIN TAddresses A WITH (NOLOCK) ON C.AddressNr = A.Serial AND C.ID = A.CustomerID 
    LEFT JOIN TOrders O WITH (NOLOCK) ON C.ID = O.CustomerID 
WHERE C.CampaignID IN(SELECT items FROM dbo.Split('196,195,210,206,205,207,204,200,209,213,197,198,214', ',')) AND 
(C.InsDate > '2011-04-03 00:00:00' OR O.CustomerID NOT NULL OR O.[Date] > '2011-04-03 00:00:00' OR O.Exported IS NULL); 

这导致数分钟的执行时间。 如果我只是从TOrders运行有趣的部分,它只需要几秒钟执行。

SELECT CustomerID 
FROM TOrders 
WHERE O.CustomerID NOT NULL 
    OR O.[Date] > '2011-04-03 00:00:00' 
    OR O.Exported IS NULL; 

所以问题是当两者结合时。我试着运行查询并直接将生成的CustomerID粘贴到主查询中,这很快捷,大约需要5-10秒。我尝试从TOrders预取有趣的数据并将其放入临时表中,但这并没有让它变得更快。

CREATE TABLE #ORDERCUSTOMERS (
    CustomerID int 
); 
CREATE UNIQUE CLUSTERED INDEX IX_1 on #ORDERCUSTOMERS (CustomerID); 

INSERT #ORDERCUSTOMERS SELECT DISTINCT O.CustomerID FROM LPD1_8.dbo.TOrders O WHERE O.CampaignID IN(SELECT items FROM dbo.Split('196,195,210,206,205,207,204,200,209,213,197,198,214', ',')) AND (O.[Date] > '2011-04-03 00:00:00' OR O.Exported IS NULL); 

SELECT 
      C.ID AS cust_id, 
      C.State AS cust_state, 
      C.PrevState AS cust_previous_state, 
      C.ProjectID AS cust_imported_on_project_id, 
      C.CampaignID AS cust_imported_on_campaign_id, 
      C.Priority AS cust_priority, 
      C.Name AS cust_firstname, 
      C.Name2 AS cust_lastname, 
      C.AllocatedUser AS cust_allocated_user, 
      C.ED1 AS cust_social_security_number, 
      C.ED2 AS cust_customer_number, 
      C.ED3 AS cust_type, 
      C.ED4 AS cust_initial_fact_1, 
      C.ED5 AS cust_initial_fact_2, 
      C.ED6 AS cust_initial_fact_3, 
      C.ED7 AS cust_initial_fact_4, 
      C.ED8 AS cust_initial_fact_5, 
      C.ED9 AS cust_extra_1, 
      C.ED10 AS cust_extra_2, 
      CED2.ED11 AS cust_extra_3, 
      CED2.ED12 AS cust_extra_4, 
      CED2.ED13 AS cust_extra_5, 
      A.Serial AS address_serial, 
      A.PostAddress AS address_postal_address, 
      A.PostCode AS address_postal_code, 
      A.PostOffice AS address_city, 
      A.PhoneNr AS address_phonenumber, 
      A.FaxNr AS address_faxnumber, 
      A.EMail AS address_email, 
      A.Notes AS address_notes, 
      A.ED1 AS address_secondary_phonenumber, 
      A.ED2 AS address_origin_file, 
      A.State AS address_state 
     FROM LPD1_8.dbo.TCustomers C WITH (NOLOCK) 
    LEFT JOIN LPD1_8.dbo.TCustED2 CED2 WITH (NOLOCK) ON C.ID = CED2.CustomerID 
    LEFT JOIN LPD1_8.dbo.TAddresses A WITH (NOLOCK) ON C.AddressNr = A.Serial AND C.ID = A.CustomerID 
    LEFT JOIN #ORDERCUSTOMERS O ON C.ID = O.CustomerID 
     WHERE C.CampaignID IN(SELECT items FROM dbo.Split('196,195,210,206,205,207,204,200,209,213,197,198,214', ',')) AND 
    (C.InsDate > '2011-04-03 00:00:00' OR O.CustomerID NOT NULL); 

DROP TABLE #ORDERCUSTOMERS; 

那么你们有什么想法吗?这将驻留在一个存储过程中,所以我想尽可能避免动态SQL。否则,这可能是一种方法,从TOrders获取数据并将返回的CustomerID注入主查询的IN子句中。 但是,可能还有一种类似的方法来将一个id数组“注入”IN子句或其他东西?

+0

也许你可以先试试你的查询分析工具。 – RollingBoy 2011-04-05 07:35:42

回答

0

您在where子句中使用了订单表。这意味着查询计划员无法尽早从客户表中删除行。你有没有试过早些时候加入订单表?

SELECT 
     C.ID AS cust_id, 
     C.State AS cust_state, 
     C.PrevState AS cust_previous_state, 
     C.ProjectID AS cust_imported_on_project_id, 
     C.CampaignID AS cust_imported_on_campaign_id, 
     C.Priority AS cust_priority, 
     C.Name AS cust_firstname, 
     C.Name2 AS cust_lastname, 
     C.AllocatedUser AS cust_allocated_user, 
     C.ED1 AS cust_social_security_number, 
     C.ED2 AS cust_customer_number, 
     C.ED3 AS cust_type, 
     C.ED4 AS cust_initial_fact_1, 
     C.ED5 AS cust_initial_fact_2, 
     C.ED6 AS cust_initial_fact_3, 
     C.ED7 AS cust_initial_fact_4, 
     C.ED8 AS cust_initial_fact_5, 
     C.ED9 AS cust_extra_1, 
     C.ED10 AS cust_extra_2, 
     CED2.ED11 AS cust_extra_3, 
     CED2.ED12 AS cust_extra_4, 
     CED2.ED13 AS cust_extra_5, 
     A.Serial AS address_serial, 
     A.PostAddress AS address_postal_address, 
     A.PostCode AS address_postal_code, 
     A.PostOffice AS address_city, 
     A.PhoneNr AS address_phonenumber, 
     A.FaxNr AS address_faxnumber, 
     A.EMail AS address_email, 
     A.Notes AS address_notes, 
     A.ED1 AS address_secondary_phonenumber, 
     A.ED2 AS address_origin_file, 
     A.State AS address_state 
FROM TCustomers C WITH (NOLOCK) 
    LEFT JOIN TOrders O WITH (NOLOCK) ON C.ID = O.CustomerID 
    LEFT JOIN TCustED2 CED2 WITH (NOLOCK) ON C.ID = CED2.CustomerID 
    LEFT JOIN TAddresses A WITH (NOLOCK) ON C.AddressNr = A.Serial AND C.ID = A.CustomerID 
WHERE C.CampaignID IN(SELECT items FROM dbo.Split('196,195,210,206,205,207,204,200,209,213,197,198,214', ',')) AND 
(C.InsDate > '2011-04-03 00:00:00' OR O.CustomerID NOT NULL OR O.[Date] > '2011-04-03 00:00:00' OR O.Exported IS NULL); 
+0

这实际上已经把它降低了很多。现在它约为50秒,比3-4分钟好得多,但距离我得到的每个单独查询的3秒还差得远。我会尝试一个动态的SQL语句,看看是否更快。否则,我会去这个。该声明用于定期导入数据,因此50 sek可以。 – inquam 2011-04-05 07:51:24

+0

也许索引字段TAddresses.Serial和TCustomers.AddressNr会加速一些事情。 – davogotland 2011-04-05 08:23:21

+0

TAddresses.Serial是TAddresses聚集索引的一部分,TCustomer.AddressNr不是任何索引的一部分。但是,由于客户仍然是我从中获取的主要桌子,这会有帮助吗? – inquam 2011-04-05 08:34:55

0

您是否尝试过使用内置的SQL Server查询优化工具分析您的查询?

+0

是的,我有..大部分tim是花在主查询(98%)上。其中大部分时间都在TC客户端上的集群索引扫描和T地址上的集群索引扫描。只有#ORDERCUSTOMERS对该查询的1%负责。有趣的部分是,如果我删除了货物,整个查询从几分钟到四秒钟。所以我认为它有点误导性,并且引入#ORDERCUSTOMERS,或者当使用连接时,实际上使TCustomers和TAddresses通过索引很多次扫描。因此,托尔斯部分是坏人。 – inquam 2011-04-05 07:38:22

+0

我猜测,引入'索引视图'不是你想要做的事情吗? – 2011-04-05 07:40:20

+0

运行数据库的系统是第三方应用程序,所以我不想尽可能少地添加它。单个SP很好地隔离。我并不很熟悉'索引视图',但我想你可以构建一个视图并对其进行索引,这样查询规划人员就可以更好地了解事物如何连接?我之前主要在MySQL中工作,并且对Sybase和MSSql特定的东西有一些有限的了解。 – inquam 2011-04-05 08:13:24

0

这将驻留在存储过程中,所以我想尽可能避免动态SQL。

不好的消息。你希望像这样的动态,因为你至少需要在SP上“重新编译”,或者你的第一次运行决定你的查询计划。

这就是说,它看起来像一个昂贵的oepration。更多细节取决于查询计划。也许你MSISAN索引或两个?请发布执行计划(假定执行&)。

+0

我在所有缺失它的列上添加了索引。个别部分是活泼的,但加入时就会死亡。 – inquam 2011-04-05 07:33:49

+0

如果对此特定部分使用动态SQL(SP包含更多内容)。有没有一种方法可以将包含结果的列ID转换为逗号分隔的字符串,以便在不需要创建指针并从“SELECT id FROM foobar”遍历结果的情况下将其注入IN子句,这可能不太理想。 – inquam 2011-04-05 07:42:15

0

查询优化器的方式并不总是可以预测的。假设你有你要加入/过滤所有列的索引,我会尝试以下方法:

OR O.CustomerID NOT NULL 

出现多余的 - 因为你正在使用的列的连接条件,应该始终评估为TRUE 。这应该表示查询返回每条记录,而不管日期如何。

此外,你可能会发现,与

WHERE C.CampaignID IN(196,195,210,206,205,207,204,200,209,213,197,198,214) 

更换

WHERE C.CampaignID IN(SELECT items FROM dbo.Split('196,195,210,206,205,207,204,200,209,213,197,198,214', ',')) 

可以加快速度。我发现函数评估可以改变查询优化器以不明显的方式工作的方式。

+0

分割是采用逗号分隔字符串的字符串参数。所以这个特定的字符串只是为了说明。 – inquam 2011-04-05 09:16:06

+0

对不起 - 我不清楚。编辑以反映我建议您尝试的内容。 – 2011-04-05 13:08:22