tsql – T-SQL – 连接多个表会导致重复的行
|
我很想从以下样本 Fiddle获得以下结果. 结果: 来源表: Fiddle 我们的想法是通过查找预算和预测ID的最大数量来创建其他列,从而为每个RecordID创建一行(如果不存在该预算或预测ID的值,则将列单元格保留为空). 我尝试使用PIVOT功能,但无法获得接近一个不错的结果. 更新: 用语言:对于属于RecordID的每个BudgetID,为BDate,Result(Percentage * BAmount of Records表)和Status创建单独的列. 在我的示例中,RecordID 55在Budget表中有两个条目 – 因此需要2×3列来显示此RecordID的单行中每个日期,结果和状态. 由于RecordID 77在Budget表中具有最多(三个)条目,因此它用于在所有行上创建3×3列. 预测也是如此. 我希望你能帮助我. 谢谢. 解决方法美好的一天,
OP提供的DDL DML: DROP TABLE IF EXISTS Budget;
CREATE TABLE Budget
(BudgetID int,RecordID int,BDate date,Percentage int,[Status] varchar(50));
INSERT INTO Budget
(BudgetID,RecordID,BDate,Percentage,Status)
VALUES
(1,55,'2017-01-01',60,'ordered'),(2,'2017-03-24',40,(3,66,'2018-08-15',100,'invoiced'),(4,77,'2018-12-02',25,'paid'),(5,'2018-09-10',35,(6,'2019-07-13','ordered')
GO
DROP TABLE IF EXISTS Forecast;
CREATE TABLE Forecast
(ForecastID int,FDate date,Percentage int);
INSERT INTO Forecast
(ForecastID,FDate,Percentage)
VALUES
(1,'2020-12-01',100),'2023-05-17',25),'2024-11-28',75)
GO
DROP TABLE IF EXISTS Records;
CREATE TABLE Records
(RecordID int,BAmount int,FAmount int,Name varchar(40),Description varchar(40) )
;
INSERT INTO Records
(RecordID,BAmount,FAmount,Name,Description)
VALUES
(55,15000,33000,'Prod1','Desc1' ),(66,22000,17500,'Prod2','Desc2' ),(77,40000,44000,'Prod3','Desc3' )
GO
select * from Budget
select * from Forecast
select * from Records
让我们首先展示一个简单的静态解决方案 这基于我们知道,对于Budget表中的每个RecordID,我们最多有三行,而对于Forecast表中的每个RecordID,最多有两行.这有助于理解我接下来要展示的动态解决方案 ;With CteBudget as (
select
b.BDate,b.BudgetID,b.Percentage,b.RecordID,b.Status,RN = ROW_NUMBER() OVER (partition by b.RecordID order by b.BudgetID)
from Budget b
),CteForecast as (
select
f.FDate,f.ForecastID,f.Percentage,f.RecordID,RN = ROW_NUMBER() OVER (partition by f.RecordID order by f.ForecastID)
from Forecast f
)
select
r.RecordID,r.Name,r.Description,b1.BDate BDate1,(b1.Percentage * r.BAmount)/100 BResult1,b1.Status BStatus1,b2.BDate BDate2,(b2.Percentage * r.BAmount)/100 BResult2,b2.Status BStatus2,b3.BDate BDate3,(b3.Percentage * r.BAmount)/100 BResult3,b3.Status BStatus3,f1.FDate FDate1,(f1.Percentage * r.BAmount)/100 FResult1,f2.FDate FDate2,(f2.Percentage * r.BAmount)/100 FResult2
from Records r
left join CteBudget b1 on r.RecordID = b1.RecordID and b1.RN = 1
left join CteBudget b2 on r.RecordID = b2.RecordID and b2.RN = 2
left join CteBudget b3 on r.RecordID = b3.RecordID and b3.RN = 3
left join CteForecast f1 on r.RecordID = f1.RecordID and f1.RN = 1
left join CteForecast f2 on r.RecordID = f2.RecordID and f2.RN = 2
--where r.RecordID = 77
GO
现在我们可以呈现动态解决方案. -- Get number of columns
Declare @NumBudget tinyint
Declare @NumForecast tinyint
SELECT @NumBudget = MAX(C) FROM (
select COUNT(RecordID) C
from Budget
GROUP BY RecordID
) t
SELECT @NumForecast = MAX(C) FROM (
select COUNT(RecordID) C
from Forecast
GROUP BY RecordID
) t
---------------------------------------------
DECLARE @SQLString1 nvarchar(MAX) = '';
DECLARE @SQLString2 nvarchar(MAX) = '';
DECLARE @loop int = 1;
WHILE @loop <= @NumBudget BEGIN
SET @SQLString1 = @SQLString1 + N'
b' + CONVERT(VARCHAR(2),@loop) + '.BDate BDate' + CONVERT(VARCHAR(2),@loop) + ',(b' + CONVERT(VARCHAR(2),@loop) + '.Percentage * r.BAmount)/100 BResult' + CONVERT(VARCHAR(2),b' + CONVERT(VARCHAR(2),@loop) + '.Status BStatus' + CONVERT(VARCHAR(2),'
SET @SQLString2 = @SQLString2 + N'
left join CteBudget b' + CONVERT(VARCHAR(2),@loop) + ' on r.RecordID = b' + CONVERT(VARCHAR(2),@loop) + '.RecordID and b' + CONVERT(VARCHAR(2),@loop) + '.RN = 1'
SET @loop = @loop + 1
END
SET @loop = 1
WHILE @loop <= @NumForecast BEGIN
SET @SQLString1 = @SQLString1 + N'
f' + CONVERT(VARCHAR(2),@loop) + '.FDate FDate' + CONVERT(VARCHAR(2),(f' + CONVERT(VARCHAR(2),@loop) + '.Percentage * r.BAmount)/100 FResult' + CONVERT(VARCHAR(2),'
SET @SQLString2 = @SQLString2 + N'
left join CteForecast f' + CONVERT(VARCHAR(2),@loop) + ' on r.RecordID = f' + CONVERT(VARCHAR(2),@loop) + '.RecordID and f' + CONVERT(VARCHAR(2),@loop) + '.RN = 1'
SET @loop = @loop + 1
END
SET @SQLString1 = STUFF (@SQLString1,LEN(@SQLString1),1,'')
PRINT '/************************************************/'
PRINT @SQLString1
PRINT @SQLString2
PRINT '/************************************************/'
DECLARE @SQLString nvarchar(MAX);
SET @SQLString = N'
;With CteBudget as (
select
b.BDate,'
+ @SQLString1
+ N'
from Records r'
+ @SQLString2
print @SQLString
EXECUTE sp_executesql @SQLString
GO
索引
为了论坛(或者你称之为stackoverflow,我认为它不是一个讨论论坛界面,而是Q& A界面),我添加查询来创建CLUSTERED INDEX,我假设你在生产中有一个,并且可选NONCLUSTERED您应该测试的INDEX(我没有测试过其他选项,这意味着第一个出现在我脑海中,因此建议使用真正的DDL DML检查正确的索引). -- CLUSTERED INDEX
CREATE CLUSTERED INDEX IX_Budget_BudgetID
ON dbo.Budget (BudgetID);
GO
CREATE CLUSTERED INDEX IX_Forecast_ForecastID
ON dbo.Forecast (ForecastID);
GO
CREATE CLUSTERED INDEX IX_Records_RecordID
ON dbo.Records (RecordID);
GO
-- NONCLUSTERED INDEX
CREATE NONCLUSTERED INDEX NX_Budget_RecordID_BudgetID
ON dbo.Budget (RecordID,BudgetID);
GO
CREATE NONCLUSTERED INDEX NX_Forecast_RecordID_ForecastID
ON dbo.Forecast (RecordID,ForecastID);
GO
CREATE NONCLUSTERED INDEX NX_Records_RecordID_RecordID
ON dbo.Records (RecordID);
GO (编辑:安卓应用网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
