SQL Server查询优化:自内部联接过多
我目前正在尝试改进SQLServer上的SQL查询 我的工作台如下所示: 猫科历史 我的目标是检索他们类别的历史。 到目前为止,我正在这样做:SQL Server查询优化:自内部联接过多,sql,sql-server,sql-server-2012,query-optimization,Sql,Sql Server,Sql Server 2012,Query Optimization,我目前正在尝试改进SQLServer上的SQL查询 我的工作台如下所示: 猫科历史 我的目标是检索他们类别的历史。 到目前为止,我正在这样做: SELECT A.DATE ,COUNT(DISTINCT A.ID) AS NB_CLIENTS ,A.CATEGORY AS STARTING_CAT ,B.CATOGORY AS ENDING_CAT FROM CAT_HISTORY A INNE
SELECT A.DATE
,COUNT(DISTINCT A.ID) AS NB_CLIENTS
,A.CATEGORY AS STARTING_CAT
,B.CATOGORY AS ENDING_CAT
FROM CAT_HISTORY A
INNER JOIN CAT_HISTORY B
ON (
A.ID= B.ID
AND
(
(
A.DATE = 20121201
AND B.DATE = 20131201
)
OR
(
A.DATE = 20131201
AND B.DATE = 20141201
)
WHERE A.DATE>= 20121201 AND B.DATE<= 20141201
GROUP BY A.DATE, A.CATEGORY,B.CATEGORY
ORDER BY A.DATE, A.CATEGORY,B.CATEGORY
但问题是我有更多的日期,我为每个日期添加一个OR,大约15个不同的日期,我有大量的用户。这意味着查询有时需要15分钟才能得到结果
我相信我的内心世界是残酷的,也许有一种更优雅、更有效的方式来达到预期的结果
我的最终目标是获得一个Sankey,以查看随着时间的推移从一个类别到另一个类别的演变,我需要在到日期之间从一个类别移动到另一个类别的用户数量
使用Gordon Linoff的答案,它工作得很好,但正在计算重复项
SELECT DISTINCT DATE, CATEGORY,NEXT_CATEGORY, COUNT(*) AS NB_CLIENTS
FROM (
SELECT DISTINCT CH.*, LEAD(CATEGORY) OVER (PARTITION BY CH.ID ORDER BY DATE) AS NEXT_CATEGORY
FROM CAT_HISTORY CH
) CH
WHERE NEXT_CATEGORY IS NOT NULL
GROUP BY DATE, CATEGORY,NEXT_CATEGORY
例如:
期望
使用您的解决方案:
DATE_KEY STARTING_CAT ENDING_CAT NB_CLIENTS
----------- ----------- ----------- -----------
20121201 1 1 1
20121201 1 2 1
20121201 1 4 1
20121201 2 3 1
20131201 2 3 1
20131201 4 2 1
20131201 2 3 1
20141201 2 2 1
上次编辑:
我设法找到了一个解决办法:
SELECT DISTINCT DATE, CATEGORY,NEXT_CATEGORY, COUNT(*) AS NB_CLIENTS
FROM (
SELECT DISTINCT CH.*, LEAD(CATEGORY) OVER (PARTITION BY CH.ID ORDER BY DATE) AS NEXT_CATEGORY
FROM (SELECT DISTINCT * FROM CAT_HISTORY) CH
) CH
WHERE NEXT_CATEGORY IS NOT NULL
GROUP BY DATE, CATEGORY,NEXT_CATEGORY
如果您希望看到成对的更改,那么使用提前期而不是固定日期。在SQL Server 2012+中,您可以执行以下操作:
select date, category, next_category, count(*)
from (select ch.*,
lead(category) over (partition by id order by date) as next_category
from cat_history ch
) ch
group by date, category, next_category;
在早期版本的SQL Server中,您可以对相关子查询或应用使用类似的逻辑。如果希望看到成对的更改,请使用提前期而不是固定日期。在SQL Server 2012+中,您可以执行以下操作:
select date, category, next_category, count(*)
from (select ch.*,
lead(category) over (partition by id order by date) as next_category
from cat_history ch
) ch
group by date, category, next_category;
在早期版本的SQL Server中,您可以对相关子查询使用类似的逻辑或应用。请检查此项,我将日期字段替换为日期字段
请检查此项,我将日期字段替换为日期字段
非常感谢您的回答,它工作的很好,但我现在有问题的事实,工作表有重复。我已经用细节编辑了我的帖子。@AxelR。你应该问另一个问题,而不是改变一个已经有答案的问题。我的错,我不想垃圾邮件问题:/非常感谢你的回答,它很好,但我现在有问题的事实,工作表有重复。我已经用细节编辑了我的帖子。@AxelR。你应该问另一个问题,而不是改变一个已经有答案的问题。糟糕,我不想滥发问题:/谢谢你的回答。我在使用它时遇到了问题,因为datefield在我这边是一个整数,而查询在执行它时会返回算术溢出。究竟为什么要将日期存储为整数,这是一种非常糟糕的做法。我们接收来自不同时区的日期,并且我们希望避免日期转换,我们决定将它们的原始值作为整数插入。谢谢您的回答。我在使用它时遇到了问题,因为datefield在我这边是一个整数,而查询在执行它时会返回算术溢出。究竟为什么要将日期存储为整数,这是一种非常糟糕的做法。我们接收来自不同时区的日期,并且我们希望避免日期转换,我们决定将其原始值作为整数插入。我更新了我的答案,您有问题,因为我没有添加具有结构的样本数据。关于您的输出结果,有一件事我不明白,NB_客户端如何根据您的输入数据拥有5、1、1、13个数据,请解释一下。所以请给出更好的答案。谢谢你,我设法让它工作了。我用相应的数据更新了我的答案。我更新了我的答案,你们有问题,因为我并没有用结构添加样本数据。关于您的输出结果,有一件事我不明白,NB_客户端如何根据您的输入数据拥有5、1、1、13个数据,请解释一下。所以请给出更好的答案。谢谢你,我设法让它工作了。我已经用相应的数据更新了我的答案。
SELECT DISTINCT DATE, CATEGORY,NEXT_CATEGORY, COUNT(*) AS NB_CLIENTS
FROM (
SELECT DISTINCT CH.*, LEAD(CATEGORY) OVER (PARTITION BY CH.ID ORDER BY DATE) AS NEXT_CATEGORY
FROM (SELECT DISTINCT * FROM CAT_HISTORY) CH
) CH
WHERE NEXT_CATEGORY IS NOT NULL
GROUP BY DATE, CATEGORY,NEXT_CATEGORY
select date, category, next_category, count(*)
from (select ch.*,
lead(category) over (partition by id order by date) as next_category
from cat_history ch
) ch
group by date, category, next_category;
declare @t table(datefield date , id varchar(10) , category int )
insert into @t values
(cast( '20121201' as date) , 'A', 1),
(cast( '20121201' as date) , 'B', 1),
(cast( '20121201' as date) , 'C', 2),
(cast( '20131201' as date) , 'A', 2),
(cast( '20131201' as date) , 'B', 4),
(cast( '20131201' as date) , 'C', 3),
(cast( '20141201' as date) , 'A', 3),
(cast( '20141201' as date) , 'B', 2),
(cast( '20141201' as date) , 'C', 1)
SELECT A.datefield
,COUNT(DISTINCT A.ID) AS NB_CLIENTS
,A.CATEGORY AS STARTING_CAT
,isnull(B.CATEGORY ,0) AS ENDING_CAT
FROM @T A
left JOIN @T B
ON
(
A.ID= B.ID AND
( b.datefield = dateadd( yy, 1 , a.datefield ) )
)
-- WHERE A.datefield>= '20121201' AND ( B.datefield<= '20141201' or B.datefield is null)
GROUP BY A.datefield, A.CATEGORY,B.CATEGORY
ORDER BY A.datefield, A.CATEGORY,B.CATEGORY