【问题标题】:How to write a query using self join on a table with huge data?如何在包含大量数据的表上使用自联接编写查询?
【发布时间】:2012-05-22 14:48:13
【问题描述】:

具有如下表结构:

表 1:客户

CustID(primary key)  |  CustName(indexed)
----------------------------------
C1                       Cust1
C2                       Cust2
.                        Cust3
.                        Cust.
.                        Cust.
C10000                   Cust10000

表 2:自定义字段

FieldID (primary key) |  ID (indexed)                | FieldValue
---------------------------------------------------------------------
1                        C1                              Test 
2                        C2                              Test 
3                        C3                              Test 
4                        C4                              Test 
.                        .                               Test 
.                        .                               Test 
few millions             Z1                              Test   

"ID" column is indexed.

尝试输出以下内容;

CustID  |  Field 1  |  Field 2 | Field 3 | .... | Field N
----------------------------------------------------------

我试着写一个类似的查询

Select 
    CustID, A1.FieldValue as [Field 1], A2.FieldValue as [Field 2]
from 
    Customers 
left outer join 
    CustomFields A1 on Customers.custID = A1.ID
left outer join 
    CustomFields A2 on Customers.custID = A2.ID
left outer join 
    CustomFields An on Customers.custID = An.ID
where 
    custName like 'C%'

由于CustomFields 表有几百万条记录,所以上面的查询表现不佳。现在大约需要 10-12 秒(对于 500 个客户和 6 个字段)

我认为左外连接在这里增加了时间。任何解决问题的想法都会真的很有帮助吗?

平台:SQL Server 2005

更新:

CustomFields 表是一个通用表,它可以包含任何其他实体(供应商、项目等)的字段。

【问题讨论】:

  • 你应该从性能的角度了解为什么 EAV 表是一个糟糕的设计选择。
  • @HLGEM 我不会笼统地说 EAV 总是一个糟糕的选择。我实际上已经成功地使用了它。完美吗?当然不是。但通常这些事情是一种权衡。 sqlblog.com/blogs/aaron_bertrand/archive/2009/11/19/…

标签: sql sql-server sql-server-2008 tsql sql-server-2005


【解决方案1】:

您是否尝试过使用数据库引擎优化顾问分析查询?不能保证,但它可能会提供一两个有用的建议。

【讨论】:

    【解决方案2】:

    您可以使用单个连接来获取自定义字段,并使用聚合函数来转置它们。这样,与表 CustomFields 的联接只进行一次。

    像这样。

    SELECT
      Customers.CustID,
      MAX(CASE WHEN CF.FieldID = 1 THEN CF.FieldValue ELSE NULL END) AS Field1,
      MAX(CASE WHEN CF.FieldID = 2 THEN CF.FieldValue ELSE NULL END) AS Field2,
      MAX(CASE WHEN CF.FieldID = 3 THEN CF.FieldValue ELSE NULL END) AS Field3,
      MAX(CASE WHEN CF.FieldID = 4 THEN CF.FieldValue ELSE NULL END) AS Field4
      -- Add more...
    
      FROM Customers 
    
      LEFT OUTER JOIN CustomFields CF
      ON CF.ID = Customers.CustID
    
      WHERE Customers.CustName like 'C%'
    
      GROUP BY Customers.CustID
    

    【讨论】:

    • 我没有想到这个!将试一试并发布结果。谢谢。
    【解决方案3】:

    您的查询完全合理。但是,数据库结构已关闭。您应该有一个单独的列用于客户 ID 和字段 ID。根据编写查询的方式,您将获得与其他表中的字段一样多的 CustId 行。

    不应将它们组合成一列。我希望加入看起来像:

    left outer join
    CustomerFields cfn
    on cfn.CustId = Customers.CustId and
       cfn.FieldNum = <n>
    

    另一种可能性是性能下降是您第一次运行查询。如果你有足够的内存,那么第二次它可能会运行得更快——因为字段表在内存中。

    为了记录,我对“喜欢”有历史偏见,相信它会导致性能问题。这可能无济于事,但您可以尝试:

    where left(CustName, 1) = 'C'
    

    【讨论】:

      【解决方案4】:
          SELECT  Pvt.ID, 
                  Customers.CustName, 
                  Pvt.[1] AS Field1, 
                  Pvt.[2] AS Field2, 
                  Pvt.[3] AS Field3, 
                  Pvt.[4] AS Field4, 
                  Pvt.[5] AS Field5,
                  ...
          FROM (
                  SELECT ID, FieldID, FieldValue
                  FROM CustomFields) AS p
                  PIVOT (
                      MIN (FieldValue)
                      FOR FieldID IN ([1], [2], [3], [4], [5], ... )
              ) AS pvt
          inner join Customers ON Customers.CustID = pvt.ID
      

      【讨论】:

      • 个人不喜欢旋转,因为这样不太灵活,无法对查询进行进一步的更改。例如,如果表存储了历史值,那么日期和时间列会添加到主键中,并且修改查询并不容易。
      猜你喜欢
      • 2013-05-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-06-28
      • 2021-10-18
      • 2011-01-23
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多