仅针对结果列比较外部回复中的前两行,但最后的结果应保持外部仅将前 1 行应用于结果
Comparing top two rows in outer reply just for a result column but last result should keep outer applying top 1 row to result only
我有一个存储过程,
Declare @UserID VARCHAR(200), @URLFilter varchar(256);
SET @UserID = '298';
SET @URLFilter = 'https://myportal.toshibafirst.com/sites';
SELECT t.*
FROM(SELECT Distinct
Problems.ID, Problems.Title, Problems.URL,
Policies.ClientID, Policies.ID As PolicyID, Problems.CategoryID, ProblemCategories.Title As CategoryTitle,
ProblemImply.Effect AS LatestEffect, ProblemImply.Probability AS LatestProbability,
ProblemImply.SolutionDate AS MovementDate, DATEDIFF(DAY, ProblemImply.SolutionDate, CURRENT_TIMESTAMP) As MovementInDays,
CompanyGroups.ID As GroupID, CompanyGroups.Title As GroupTitle, SolutionStartDate, SolutionStatus, SolutionTitle,
dbo.IsUserAdmin(@UserID, ProblemCategories.ProblemGroupID) AS IsUserAdmin,
dbo.FunctionA(@UserID, ProblemCategories.ID) AS FunctionAOutput,
dbo.FunctionB(@UserID, Problems.ID) AS FunctionBOutput
From ProblemCategories
INNER JOIN Policies ON Policies.ID = ProblemCategories.PolicyID
FULL OUTER JOIN Problems ON Problems.CategoryID = ProblemCategories.ID
OUTER APPLY
(SELECT TOP 1 * FROM Problems_Imply WHERE Problems_Imply.ProblemID = Problems.ID
AND Problems_Imply.Description <> 'Target' AND Problems_Imply.Effect > -1 AND Problems_Imply.Probability > -1
ORDER BY Problems_Imply.SolutionDate Desc) As ProblemImply
OUTER APPLY
(SELECT Problems_Solutions.StartDate AS SolutionStartDate, Problems_Solutions.Status AS SolutionStatus, Problems_Solutions.GroupID, Problems_Solutions.Title AS SolutionTitle
FROM Problems_Solutions WHERE ProblemID = Problems.ID AND Status NOT IN ('Cancelled')) As ProblemSolution
LEFT JOIN CompanyGroups ON ProblemSolution.GroupID = CompanyGroups.ID
WHERE Problems.ID IS NOT NULL AND ProblemCategories.URL LIKE '%' + @URLFilter + '%' AND Problems.Status LIKE 'Open') t
WHERE (t.IsUserAdmin > 0 OR t.FunctionAOutput > 0 OR t.FunctionBOutput > 0) AND t.SolutionTitle IS NOT NULL
它给了我完美的结果,但现在我必须改变它,我以某种方式改变它,以便在下面的外部应用中,
OUTER APPLY
(SELECT TOP 1 * FROM Problems_Imply WHERE Problems_Imply.ProblemID = Problems.ID
AND Problems_Imply.Description <> 'Target' AND Problems_Imply.Effect > -1 AND Problems_Imply.Probability > -1
ORDER BY Problems_Imply.SolutionDate Desc) As ProblemImply
我想在 t
中添加另一个结果,如下所示,
@Compare = (TopFirstRow.Effect * TopFirstRow.Probability) > (TopSecondRow.Effect * TopSecondRow.Probability)
OR it remained same or if it decreased.
我需要在外部 APPLY 中调整这个 @Compare,所以我为每一行都获取了它。但是我不希望我的结果发生变化,然后得到另一列@Compare。
我可以考虑添加另一个外部应用并再次调用此 table 然后计算差异,但是有没有更好的方法来做到这一点?
只是为了让它更有意义,我需要检查 Effect * Probability 在比较第一行和第二行时是否增加、减少或保持不变
您的 APPLY 没有外部引用,因此您可以将其更改为普通连接,因此我只需将您的查询更改为:
WITH Top2Fruits AS
( SELECT TOP 2
ID,
Description,
Weight,
Size,
DaysLeft,
RowNum = ROW_NUMBER() OVER(ORDER BY ExpiryDate DESC)
FROM Fruits
WHERE Fruits.ID = @ID
ORDER BY ExpiryDate DESC
)
SELECT <columns>,
Compare = CASE WHEN (f1.Weight * f1.Size) > (f2.Weight * f2.Size) THEN 1 ELSE 0 END
FROM <tables>
LEFT JOIN Top2Fruits AS f1
ON f1.RowNum = 1
LEFT JOIN Top2Fruits AS f2
ON f1.RowNum = 2;
现在您可以分别访问派生表 f1 和 f2 中的第一个和第二个水果。
附录
即使使用外部引用,您仍然可以使用 ROW_NUMBER()
和 LEFT JOIN
:
WITH ProblemImply AS
(
SELECT ProblemID,
Effect,
Probability,
SolutionDate,
RowNum = ROW_NUMBER() PARTITION BY ProblemID ORDER BY SolutionDate DESC)
FROM Problems_Imply
WHERE Description <> 'Target'
AND Effect > -1
AND Probability > -1
), DistinctData AS
(
SELECT pr.ID,
pr.Title,
pr.URL,
po.ClientID,
po.ID AS PolicyID,
pr.CategoryID,
pc.Title AS CategoryTitle,
pi1.Effect AS LatestEffect,
pi1.Probability AS LatestProbability,
pi1.SolutionDate AS MovementDate,
pi2.Effect AS PreviousEffect,
pi2.Probability AS PreviousProbability,
pi2.SolutionDate AS PreviousMovementDate,
DATEDIFF(DAY, pi1.SolutionDate, CURRENT_TIMESTAMP) As MovementInDays,
cg.ID As GroupID,
cg.Title As GroupTitle,
ps.StartDate AS SolutionStartDate,
ps.Status AS SolutionStatus,
ps.Title AS SolutionTitle,
dbo.IsUserAdmin(@UserID, pc.ProblemGroupID) AS IsUserAdmin,
dbo.FunctionA(@UserID, pc.ID) AS FunctionAOutput,
dbo.FunctionB(@UserID, pr.ID) AS FunctionBOutput,
CASE SIGN((pi1.Effect * pi1.Probability) - (pi2.Effect * pi2.Probability))
WHEN 1 THEN 'Increase'
WHEN 0 THEN 'Static'
WHEN -1 THEN 'Decrease'
ELSE 'No Previous Data'
END AS Compare
FROM ProblemCategories AS pc
INNER JOIN Policies AS po
ON po.ID = pc.PolicyID
FULL OUTER JOIN Problems AS pr
ON pr.CategoryID = pc.ID
LEFT JOIN ProblemImply AS pi1
ON pi1.ProblemID = pr.ID
AND pi1.RowNum = 1
LEFT JOIN ProblemImply AS pi2
ON pi2.ProblemID = pr.ID
AND pi2.RowNum = 2
LEFT JOIN Problems_Solutions AS ps
ON ps.ProblemID = pr.ID
AND ps.Status NOT IN ('Cancelled')
LEFT JOIN CompanyGroups AS cg
ON ps.GroupID = cg.ID
WHERE pr.ID IS NOT NULL
AND pc.URL LIKE '%' + @URLFilter + '%'
AND pr.Status LIKE 'Open'
) t
SELECT *
FROM DistinctData AS t
WHERE (t.IsUserAdmin > 0 OR t.FunctionAOutput > 0 OR t.FunctionBOutput > 0)
AND t.SolutionTitle IS NOT NULL;
我使用了别名来使查询更清晰一些,但我可能有几个错误,很抱歉,但是没有数据来测试很难:)
一般有2个版本,outer apply和join。当结果不改变行数时,我更喜欢外部应用。
并且您可以 select 前 2 行的不同方式:
with cte as (
select *
, rn = row_number() over ( partition by id order by somedate)
from y
)
select *
from x
left join cte on x.id=cte.id
left join cte cte2 on cte.id=cte2.id and cte.rn=1 and cte2.rn=2
select *
from x
outer apply (
select top 1 *
from y
where x.id=y.id
order by somedate
) cte
outer apply (
select top 1 *
from y
where x.id=y.id and y.somedate>cte.somedate
order by somedate
) cte2
两者都有同样的问题,结果不是 predictable,如果某个日期(或您用来排序的任何列)不是每个 id[=12 唯一的,则结果可能不正确=]
并且执行计划会有所不同。 'outer apply` 可能会导致嵌套循环,连接可能会与查询中的其他连接混淆,并且可能会过早执行(ID 太多)。两个查询都命中了 cte 两次。
因为您已经有一个相当复杂的查询并且对行 predictable 进行排序有些困难并且您在存储过程中,我会选择 table 变量。我经常发现如果我将巨大的查询分成更小的部分并且搜索错误也更容易,我的存储过程 运行 会更快。
declare cte as table
( id int not null
, rn int not null
, primary key (id,rn)
, data something
, ...
);
declare rows as table
( id int not null
, data something
, ...
);
insert into @rows (...)
select * from your rather_large_query;
with cte as (
select *
, rn = row_number() over ( partition by id order by somedate)
from y
)
insert into @cte ( id, rn, data, ...)
select *
from cte
where rn in (1,2) and id in (select id from @rows);
select *
from @rows x
left join @cte cte on x.id=cte.id
left join @cte cte2 on cte.id=cte2.id and cte.rn=1 and cte2.rn=2
希望对您有所帮助
我有一个存储过程,
Declare @UserID VARCHAR(200), @URLFilter varchar(256);
SET @UserID = '298';
SET @URLFilter = 'https://myportal.toshibafirst.com/sites';
SELECT t.*
FROM(SELECT Distinct
Problems.ID, Problems.Title, Problems.URL,
Policies.ClientID, Policies.ID As PolicyID, Problems.CategoryID, ProblemCategories.Title As CategoryTitle,
ProblemImply.Effect AS LatestEffect, ProblemImply.Probability AS LatestProbability,
ProblemImply.SolutionDate AS MovementDate, DATEDIFF(DAY, ProblemImply.SolutionDate, CURRENT_TIMESTAMP) As MovementInDays,
CompanyGroups.ID As GroupID, CompanyGroups.Title As GroupTitle, SolutionStartDate, SolutionStatus, SolutionTitle,
dbo.IsUserAdmin(@UserID, ProblemCategories.ProblemGroupID) AS IsUserAdmin,
dbo.FunctionA(@UserID, ProblemCategories.ID) AS FunctionAOutput,
dbo.FunctionB(@UserID, Problems.ID) AS FunctionBOutput
From ProblemCategories
INNER JOIN Policies ON Policies.ID = ProblemCategories.PolicyID
FULL OUTER JOIN Problems ON Problems.CategoryID = ProblemCategories.ID
OUTER APPLY
(SELECT TOP 1 * FROM Problems_Imply WHERE Problems_Imply.ProblemID = Problems.ID
AND Problems_Imply.Description <> 'Target' AND Problems_Imply.Effect > -1 AND Problems_Imply.Probability > -1
ORDER BY Problems_Imply.SolutionDate Desc) As ProblemImply
OUTER APPLY
(SELECT Problems_Solutions.StartDate AS SolutionStartDate, Problems_Solutions.Status AS SolutionStatus, Problems_Solutions.GroupID, Problems_Solutions.Title AS SolutionTitle
FROM Problems_Solutions WHERE ProblemID = Problems.ID AND Status NOT IN ('Cancelled')) As ProblemSolution
LEFT JOIN CompanyGroups ON ProblemSolution.GroupID = CompanyGroups.ID
WHERE Problems.ID IS NOT NULL AND ProblemCategories.URL LIKE '%' + @URLFilter + '%' AND Problems.Status LIKE 'Open') t
WHERE (t.IsUserAdmin > 0 OR t.FunctionAOutput > 0 OR t.FunctionBOutput > 0) AND t.SolutionTitle IS NOT NULL
它给了我完美的结果,但现在我必须改变它,我以某种方式改变它,以便在下面的外部应用中,
OUTER APPLY
(SELECT TOP 1 * FROM Problems_Imply WHERE Problems_Imply.ProblemID = Problems.ID
AND Problems_Imply.Description <> 'Target' AND Problems_Imply.Effect > -1 AND Problems_Imply.Probability > -1
ORDER BY Problems_Imply.SolutionDate Desc) As ProblemImply
我想在 t
中添加另一个结果,如下所示,
@Compare = (TopFirstRow.Effect * TopFirstRow.Probability) > (TopSecondRow.Effect * TopSecondRow.Probability)
OR it remained same or if it decreased.
我需要在外部 APPLY 中调整这个 @Compare,所以我为每一行都获取了它。但是我不希望我的结果发生变化,然后得到另一列@Compare。
我可以考虑添加另一个外部应用并再次调用此 table 然后计算差异,但是有没有更好的方法来做到这一点?
只是为了让它更有意义,我需要检查 Effect * Probability 在比较第一行和第二行时是否增加、减少或保持不变
您的 APPLY 没有外部引用,因此您可以将其更改为普通连接,因此我只需将您的查询更改为:
WITH Top2Fruits AS
( SELECT TOP 2
ID,
Description,
Weight,
Size,
DaysLeft,
RowNum = ROW_NUMBER() OVER(ORDER BY ExpiryDate DESC)
FROM Fruits
WHERE Fruits.ID = @ID
ORDER BY ExpiryDate DESC
)
SELECT <columns>,
Compare = CASE WHEN (f1.Weight * f1.Size) > (f2.Weight * f2.Size) THEN 1 ELSE 0 END
FROM <tables>
LEFT JOIN Top2Fruits AS f1
ON f1.RowNum = 1
LEFT JOIN Top2Fruits AS f2
ON f1.RowNum = 2;
现在您可以分别访问派生表 f1 和 f2 中的第一个和第二个水果。
附录
即使使用外部引用,您仍然可以使用 ROW_NUMBER()
和 LEFT JOIN
:
WITH ProblemImply AS
(
SELECT ProblemID,
Effect,
Probability,
SolutionDate,
RowNum = ROW_NUMBER() PARTITION BY ProblemID ORDER BY SolutionDate DESC)
FROM Problems_Imply
WHERE Description <> 'Target'
AND Effect > -1
AND Probability > -1
), DistinctData AS
(
SELECT pr.ID,
pr.Title,
pr.URL,
po.ClientID,
po.ID AS PolicyID,
pr.CategoryID,
pc.Title AS CategoryTitle,
pi1.Effect AS LatestEffect,
pi1.Probability AS LatestProbability,
pi1.SolutionDate AS MovementDate,
pi2.Effect AS PreviousEffect,
pi2.Probability AS PreviousProbability,
pi2.SolutionDate AS PreviousMovementDate,
DATEDIFF(DAY, pi1.SolutionDate, CURRENT_TIMESTAMP) As MovementInDays,
cg.ID As GroupID,
cg.Title As GroupTitle,
ps.StartDate AS SolutionStartDate,
ps.Status AS SolutionStatus,
ps.Title AS SolutionTitle,
dbo.IsUserAdmin(@UserID, pc.ProblemGroupID) AS IsUserAdmin,
dbo.FunctionA(@UserID, pc.ID) AS FunctionAOutput,
dbo.FunctionB(@UserID, pr.ID) AS FunctionBOutput,
CASE SIGN((pi1.Effect * pi1.Probability) - (pi2.Effect * pi2.Probability))
WHEN 1 THEN 'Increase'
WHEN 0 THEN 'Static'
WHEN -1 THEN 'Decrease'
ELSE 'No Previous Data'
END AS Compare
FROM ProblemCategories AS pc
INNER JOIN Policies AS po
ON po.ID = pc.PolicyID
FULL OUTER JOIN Problems AS pr
ON pr.CategoryID = pc.ID
LEFT JOIN ProblemImply AS pi1
ON pi1.ProblemID = pr.ID
AND pi1.RowNum = 1
LEFT JOIN ProblemImply AS pi2
ON pi2.ProblemID = pr.ID
AND pi2.RowNum = 2
LEFT JOIN Problems_Solutions AS ps
ON ps.ProblemID = pr.ID
AND ps.Status NOT IN ('Cancelled')
LEFT JOIN CompanyGroups AS cg
ON ps.GroupID = cg.ID
WHERE pr.ID IS NOT NULL
AND pc.URL LIKE '%' + @URLFilter + '%'
AND pr.Status LIKE 'Open'
) t
SELECT *
FROM DistinctData AS t
WHERE (t.IsUserAdmin > 0 OR t.FunctionAOutput > 0 OR t.FunctionBOutput > 0)
AND t.SolutionTitle IS NOT NULL;
我使用了别名来使查询更清晰一些,但我可能有几个错误,很抱歉,但是没有数据来测试很难:)
一般有2个版本,outer apply和join。当结果不改变行数时,我更喜欢外部应用。
并且您可以 select 前 2 行的不同方式:
with cte as (
select *
, rn = row_number() over ( partition by id order by somedate)
from y
)
select *
from x
left join cte on x.id=cte.id
left join cte cte2 on cte.id=cte2.id and cte.rn=1 and cte2.rn=2
select *
from x
outer apply (
select top 1 *
from y
where x.id=y.id
order by somedate
) cte
outer apply (
select top 1 *
from y
where x.id=y.id and y.somedate>cte.somedate
order by somedate
) cte2
两者都有同样的问题,结果不是 predictable,如果某个日期(或您用来排序的任何列)不是每个 id[=12 唯一的,则结果可能不正确=]
并且执行计划会有所不同。 'outer apply` 可能会导致嵌套循环,连接可能会与查询中的其他连接混淆,并且可能会过早执行(ID 太多)。两个查询都命中了 cte 两次。
因为您已经有一个相当复杂的查询并且对行 predictable 进行排序有些困难并且您在存储过程中,我会选择 table 变量。我经常发现如果我将巨大的查询分成更小的部分并且搜索错误也更容易,我的存储过程 运行 会更快。
declare cte as table
( id int not null
, rn int not null
, primary key (id,rn)
, data something
, ...
);
declare rows as table
( id int not null
, data something
, ...
);
insert into @rows (...)
select * from your rather_large_query;
with cte as (
select *
, rn = row_number() over ( partition by id order by somedate)
from y
)
insert into @cte ( id, rn, data, ...)
select *
from cte
where rn in (1,2) and id in (select id from @rows);
select *
from @rows x
left join @cte cte on x.id=cte.id
left join @cte cte2 on cte.id=cte2.id and cte.rn=1 and cte2.rn=2
希望对您有所帮助