SQL Server加入问题/备用方式

时间:2017-01-19 22:16:51

标签: sql-server join

我有一个查询,我试图找出客户ID之间的链关系。目前约有8万条记录。 7分钟。你能否提出一些其他改进方法?

样本格式如下所示。在这里,我们基于具有它们之间关系的记录进行分组(a = b = c)

Create table #chaintable
(
    CustID int, 
    MatchCustID int, 
    FN varchar(10), 
    LN varchar(10), 
    PhoneNo int, 
    Email varchar(50), 
    dtAppointment int
)

insert into #chaintable
Select 1,2,'Global','Chain',123,'',1
union all
Select 2,3,'Global','Chain',123,'a@a.com',2
union all
Select 3,2,'Global','Chain',567,'a@a.com',3
union all
Select 4,5,'Global1','Chain1',123,'a@a.com',1
union all
Select 5,4,'Global1','Chain1',123,'a@a.com',2

Select distinct 
    A.CustID, A.MatchCustID, A.GroupID  
from 
    (select 
         c1.CustID, c1.MatchCustID, C1.dtAppointment,
         case 
            when C1.CustID = C2.MatchCustID and C1.MatchCustID <> C2.CustID 
               then C1.CustID
            when C1.CustID <> C2.MatchCustID and C1.MatchCustID = C2.CustID 
               then c1.MatchCustID
            when C1.CustID = C2.MatchCustID and C1.MatchCustID = C2.CustID 
               then 
                  case 
                     when c1.CustID < C1.MatchCustID 
                        then c1.CustID
                        else c1.MatchCustID
                  end
         end GroupID
     from 
         #chaintable C1, #chaintable C2
     where 
         c1.CustID = c2.MatchCustID 
         or c1.MatchCustID = c2.CustID) A

输出:

CustID  MatchCustID FN  LN  PhoneNo Email   dtAppointment
---------------------------------------------------------    
    1   2   Global  Chain   123         1

    2   3   Global  Chain   123 a@a.com 2

    3   2   Global  Chain   567 a@a.com 3
    4   5   Global1 Chain1  123 a@a.com 1
    5   4   Global1 Chain1  123 a@a.com 2

2 个答案:

答案 0 :(得分:0)

首先,在不知道执行计划的情况下,无法帮助提高查询的性能。

这里有一些我不明白的问题。例如,为什么要连接到表本身,所有输出都是第一个表的值。加入真的有必要吗?或者仅仅是为了测试?

我建议以下&#34;在逻辑上等同于&#34;在不使用JOIN中的OR的情况下重写查询的方法,以及更少的工作来理解对人的查询(如果计算机感觉相同,那么它可能会改进)。

SELECT DISTINCT c1.CustID, c1.MatchCustID,
CASE WHEN (C1.MatchCustID <> c2.CustID) 
       OR (c1.CustID < c1.MatchCustID) THEN c1.CustID
     ELSE c1.MatchCustID END AS GroupID
FROM #chaintable C1 JOIN  #chaintable C2
ON c1.CustID = c2.MatchCustID

UNION

SELECT c1.CustID, c1.MatchCustID,
   c1.MatchCustID AS GroupID
FROM #chaintable C1 JOIN  #chaintable C2
ON c2.CustID = c1.MatchCustID AND C1.CustID<>C2.MatchCustID

答案 1 :(得分:0)

首先,在向表中添加行时尝试坚持标准。虽然UNION ALL可能足以处理您的简单行,但对于您提到的大量插入,它似乎相当冗长。但是,为此,请确保将它们视为关系数据集,并避免不必要的步骤。

此外,CARTESIAN JOIN是旧的SQL语法,今天的OUTERINNER JOIN更加精通,因此这种旧的连接方式只适用于特殊情况。这不是其中之一。

查看您的表格和结果,会发现以下有关您的表格结构的信息:

  • CustID独立于此表。
  • MatchCustID没有限制订单
  • 约会是耐用的钥匙。
  • FN和LN一起形成一个指示GROUPID
  • 的ID

那么,解决方案可能如下:

Create table #chaintable
(
    CustID int, MatchCustID int, FN varchar(10), LN varchar(10)
  , PhoneNo, Email varchar(50), dtAppointment int
) 

INSERT INTO #chaintable
VALUES (1,2,'Global','Chain',123,'',1)
     , (2,3,'Global','Chain',123,'a@a.com',2)
     , (3,2,'Global','Chain',567,'a@a.com',3)
     , (4,5,'Global1','Chain1',123,'a@a.com',1)
     , (5,4,'Global1','Chain1',123,'a@a.com',2)

SELECT CustID
      ,MatchCustID
      ,dtAppointment
      , FN
      , LN
      , DENSE_RANK() OVER (ORDER BY FN + LN DESC ) AS GroupID
FROM #chaintable

Resuls:

CustID  MatchCustID dtAppointment     FN       LN   GroupID
1               2         1         Global    Chain     1
2               3         2         Global    Chain     1
3               2         3         Global    Chain     1
4               5         1         Global1   Chain1    2
5               4         2         Global1   Chain1    2

这里唯一的问题是如何使用唯一ID。在此示例中,由于我没有唯一标识事件链的值,因此我使用FN + LN来恢复订单。

这有几个好处:

  1. 您可以通过一次遍历行来避免代价高昂的Cartesian JOIN
  2. GROUPID决赛桌中每个小组的内容始终相同。
  3. 执行预先检查并不困难:
  4. DECLARE @GROUPID = (SELECT MAX(GROUPID) FROM <SourceTable> )
    

    然而,这也有缺点:

    • 添加到此表可能需要您拆分insert语句以检查现有的GROUPID(遗憾的是您的数据尚未存在)。在您的情况下,确定GROUPID的列是FN + LN。
    • 我建议使用临时表来获取唯一的FN + LN值,然后运行外部应用操作,例如

    实施例

    SELECT FN + LN FROM #chaintable A
    WHERE NOT EXISTS (SELECT 1
                      FROM #chaintable
                      WHERE A.FN = FN
                        AND A.LN = LN)
    

    在运行insert语句之前添加我们之前在insert语句中检查的前置值:

    DECLARE @GROUPID = (SELECT ISNULL(MAX(GROUPID), 0) FROM <SourceTable> )
    INSERT INTO FINAL_TABLE (CustID, MatchCustID, FN, LN, PhoneNo, Email, dtAppointment)
    SELECT  CustID
          , MatchCustID
          , FN
          , LN
          , PhoneNo
          , Email
          , dtAppointment
          , @GROUPID + DENSE_RANK() OVER (ORDER BY FN + LN DESC ) AS GroupID
    FROM #chaintable_sub
    

    这将始终导致下一个GROUPID的数量大于之前的条目。

    最后,我建议您按原样处理这些数据:Dirty Data。你必须对它进行ETL转换,特别是因为你有一个带有复合ID密钥的持久密钥......所以基本上是一个FACT表。