【问题标题】:Is there a more efficient way to write this SQL query?有没有更有效的方法来编写这个 SQL 查询?
【发布时间】:2015-05-31 22:46:25
【问题描述】:

我有一张表,里面有几百万条记录,按 SessionGUID(访问者)存储产品视图,表结构是:

ID (PK) SessionGUID(唯一标识符) 产品 ID(整数) 日期时间(日期时间)

查询的目的是返回查看特定产品 ID 的同一个人查看的产品 ID 列表(即,查看 X 的客户也查看了 Y)

我使用的查询如下:

SELECT
A.ProductID
FROM
VISITOR_SESSIONS_PRODUCTVIEWS A
WHERE
A.SessionGUID IN (SELECT DISTINCT SessionGUID FROM VISITOR_SESSIONS_PRODUCTVIEWS WHERE ProductID = @ProductID)
GROUP BY
A.ProductID
ORDER BY
COUNT(A.ProductID) DESC

是否有更有效的方法来使用 GROUP BY/HAVING/PARTITON 或其他更优化的方法来以我需要的方式获取数据?

我在我的开发服务器上运行 SQL 2008,但当它上线时将从 SQL Azure 运行。

【问题讨论】:

  • 我原来有它,但把它拿出来看看它是否提高了性能。它似乎没有任何影响(因为这是存储过程的一部分,我可以在将数据返回到应用程序的实际选择中过滤掉它)
  • 如果您也可以提供一些示例数据和示例输出,将会有所帮助

标签: sql sql-server tsql azure-sql-database


【解决方案1】:

通常,EXISTSIN 更高效:

SELECT A.ProductID
FROM VISITOR_SESSIONS_PRODUCTVIEWS A
WHERE EXISTS (SELECT 1
              FROM VISITOR_SESSIONS_PRODUCTVIEWS B
              WHERE B.ProductID = @ProductID AND
                    A.SessionGUID = B.SessionGUID
             )
GROUP BY A.ProductID
ORDER BY COUNT(A.ProductID) DESC;

为了获得最佳性能,您需要在VISITOR_SESSIONS_PRODUCTVIEWS(SessionGUID, ProductId) 上建立索引,也许还需要在VISITOR_SESSIONS_PRODUCTVIEWS(ProductId) 上。

编辑:

您可以尝试使用窗口函数编写此代码,但我不确定性能会更好:

select productid
from (select pv.*,
             sum(case when productid = @productid then 1 else 0 end) over (partition by SessionGUID) as cnt
      from visitor_sessions_productviews 
     ) pv
where cnt > 0
group by productid
order by count(*) desc;

我不确定性能会比EXISTS 方法更好。

【讨论】:

  • 有趣,我检查了索引,没有你建议的 SessionGUID,ProductID 索引 - 我有: ( [ProductID] ASC ) INCLUDE ( [SessionGUID]) 。添加 SessionGUID,ProductID 作为自己的索引将 IN 和 EXISTS 查询的查询时间缩短了一半。
【解决方案2】:

您可以通过几种不同的方式进行编写,但效果可能会更好:

  • 临时会话表,然后通过这些会话加入 VISITOR_SESSIONS_PRODUCTVIEWS
  • 使用 Product 加入 VISITOR_SESSIONS_PRODUCTVIEWS 会话,然后使用这些会话重新加入 VISITOR_SESSIONS_PRODUCTVIEWS
  • EXISTS 而不是 DISTINCT 查询该用户会话的相关产品是否存在

这里是临时表解决方案:

SELECT DISTINCT SessionGUID 
INTO #sessionsWithProduct
FROM VISITOR_SESSIONS_PRODUCTVIEWS
WHERE ProductID = @ProductID;

SELECT
    A.ProductID
FROM VISITOR_SESSIONS_PRODUCTVIEWS A
    INNER JOIN #sessionsWithProduct S ON s.SessionGUID = A.SessionGUID
GROUP BY A.ProductID
ORDER BY COUNT(A.ProductID) DESC;

DROP TABLE #sessionsWithProduct;

另外非常重要的是确保此表在 ProductID 和 SessionGUID(各一个)上至少被索引。

【讨论】:

    【解决方案3】:
    SELECT A.ProductID
      FROM VISITOR_SESSIONS_PRODUCTVIEWS A
      JOIN VISITOR_SESSIONS_PRODUCTVIEWS S
        ON A.SessionGUID = S.SessionGUID 
       AND S.ProductID = @ProductID 
     GROUP B A.ProductID
     ORDER BY COUNT(DISTINCT(A.ProductID)) DESC
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多