我正在创建一个视图,用作报告,将各个表中的行数据显示为列,在某些情况下涉及连接。 当我使用临时表时,我的查询效果很好,但它需要是一个视图,我正在努力将临时表方法转换为子查询。
我已经创建了几个示例表和数据,以尽可能简化这个问题。 这是架构:
CREATE TABLE [Car] (
[CarId] [int] NOT NULL,
[Name] [varchar](50) NOT NULL,
CONSTRAINT [PK_Car] PRIMARY KEY CLUSTERED ([CarId] ASC))
INSERT INTO [Car] VALUES
(1, 'Honda')
CREATE TABLE [Part] (
[PartId] [int] IDENTITY(1,1) NOT NULL,
[CarId] [int] NOT NULL,
[Name] [varchar](50) NOT NULL,
[PercentComplete] [decimal](3, 2) NOT NULL,
CONSTRAINT [PK_Part] PRIMARY KEY CLUSTERED ([PartId] ASC))
INSERT INTO [Part] VALUES
(1, 'Engine', 0.5)
,(1, 'Transmission', 0.75)
,(1, 'Suspension', 0.3)
这是最初的步骤:
SELECT
c.CarId
,c.Name AS [CarName]
,p.Name AS [PartName]
,p.PercentComplete AS [PartPercentComplete]
INTO #Temp
FROM Car c
JOIN Part p
ON p.CarId = c.CarId
以下是将其转换为报告视图的分组选择:
SELECT
MAX(CarName) AS [Car Name]
,STUFF((
SELECT ', ' + [PartName]
FROM #Temp
WHERE (CarId = t1.CarId)
FOR XML PATH (''))
,1,2,'') AS [Parts]
,MAX(CASE [PartCompleteAvg] WHEN 1.00 THEN 'Complete' ELSE 'Incomplete' END) AS [Part Status]
,MAX(CASE [PartId] WHEN [LatestPartId] THEN [PartName] ELSE NULL END) AS [Latest Part]
FROM (
SELECT
CarId
,AVG(PartPercentComplete) AS [PartCompleteAvg]
,MAX(PartId) AS [LatestPartId]
FROM #Temp
GROUP BY CarId
) t1
LEFT JOIN #Temp t2
ON t2.CarId = t1.CarId
GROUP BY t1.CarId
除非我因为中间聚合函数AVG(PartPercentComplete)
而无法将其转换为视图,并且使用STUFF
连接技巧,否则它的效果非常好。我的报告要求其中的几个。
我知道我无法在AVG()
中嵌套MAX()
因为Cannot perform an aggregate function on an expression containing an aggregate or a subquery.
(仅供参考我知道PIVOT
,但它需要超过multiple columns,虽然我也意识到这一点,CASE
似乎更容易I'm told faster。 )
我完全错误和不良的尝试:
SELECT
MAX(CarName) AS [Car Name]
,STUFF((
SELECT ', ' + [PartName]
FROM #Temp
WHERE (CarId = t1.CarId)
FOR XML PATH (''))
,1,2,'') AS [Parts]
,MAX(CASE [PartCompleteAvg] WHEN 1.00 THEN 'Complete' ELSE 'Incomplete' END) AS [Part Status]
FROM (
SELECT
CarId
,AVG(PartPercentComplete) AS [PartCompleteAvg]
FROM (
SELECT
c.CarId
,c.Name AS [CarName]
,p.Name AS [PartName]
,p.PercentComplete AS [PartPercentComplete]
FROM Car c
JOIN Part p
ON p.CarId = c.CarId
) t1
GROUP BY CarId
) t1
LEFT JOIN #Temp t2
ON t2.CarId = t1.CarId
GROUP BY t1.CarId
显然#Temp
在两个地方都无效。我不是SQL专家,我希望有一个干净,相对简单的方法,甚至可以避免自我加入。
修改
我加入了
MAX(PartId) AS [LatestPartId]
和
MAX(CASE [PartId] WHEN [LatestPartId] THEN [PartName] ELSE NULL END) AS [Latest Part]
因为它是我需要中间GROUP BY
的一个更好的例子。
答案 0 :(得分:2)
也许我在您的要求中遗漏了一些内容,但这对您不起作用(在SQL Server 2008中测试并使用它创建了一个视图):
select max(c.name) CarName,
STUFF((
SELECT ', ' + p.Name
from part p
WHERE (p.CarId = c.CarId)
FOR XML PATH (''))
,1,2,'') AS [Parts],
CASE AVG(p.PercentComplete) WHEN 1.00 THEN 'Complete' ELSE 'Incomplete' END AS [Part Status]
from car c
inner join part p
on c.carid = p.carid
group by c.carid
答案 1 :(得分:1)
以下是更新后问题的更新答案。
;WITH A(CarID,CarName,PartName,IsComplete,RN)AS(
SELECT C.CarID, C.Name, P.Name, case when P.PercentComplete=1 then 1 else 0 end,
rn=row_number() over (partition by C.CarID order by P.PartId)
From Car C
LEFT JOIN Part P on P.CarId = C.CarId
)
,B(CarID,CarName,PartNames,LastPartName,IsComplete,RN)AS(
select CarID,CarName,CAST(PartName as VARCHAR(max)),PartName,IsComplete,RN
from A
Where rn=1
union all
select A.CarID,A.CarName,B.PartNames+', '+A.PartName,A.PartName,Case when A.IsComplete=1 and B.IsComplete=1 then 1 else 0 end,A.RN
from B
join A on A.CarID=B.CarID and A.RN-1=B.RN
),C AS(
select *, RNc=Row_number() over (partition by CarID order by RN desc)
from B
)
select CarID,CarName,PartNames,LastPartName,IsComplete
from C
where RNc=1
在此之下,原始答案。
CREATE TABLE [Car] (
[CarId] [int] NOT NULL,
[Name] [varchar](50) NOT NULL,
CONSTRAINT [PK_Car] PRIMARY KEY CLUSTERED ([CarId] ASC));
INSERT INTO [Car] VALUES (1, 'Honda'),
(2, 'Ford');
CREATE TABLE [Part] (
[PartId] [int] IDENTITY(1,1) NOT NULL,
[CarId] [int] NOT NULL,
[Name] [varchar](50) NOT NULL,
[PercentComplete] [decimal](3, 2) NOT NULL,
CONSTRAINT [PK_Part] PRIMARY KEY CLUSTERED ([PartId] ASC));
INSERT INTO [Part] VALUES (1, 'Engine', 0.5),
(1, 'Transmission', 0.75),
(1, 'Suspension', 0.3),
(2, 'Engine', 1),
(2, 'Brake', 1.0);
SELECT C.CarID,
C.Name CarName,
STUFF((
SELECT ', ' + Name
FROM Part
WHERE CarId = C.CarId
FOR XML PATH ('')),1,2,'') Parts,
CASE WHEN Progress = 1 then 'Complete' else 'Incomplete' END [Part Status]
From Car C
LEFT JOIN (
select CarId, SUM(PercentComplete)/Count(1) Progress
From Part
Group by CarId) P on P.CarID = C.CarId
CarID CarName Parts Part Status
----------- -------- ----------------------------------- -----------
1 Honda Engine, Transmission, Suspension Incomplete
2 Ford Engine, Brake Complete
答案 2 :(得分:1)
SELECT c.CarId
,c.Name AS [CarName]
,STUFF((
SELECT ', ' + Name
FROM Part p
WHERE p.CarId = c.CarId
FOR XML PATH ('')),1,2,'') AS [Parts]
,CASE WHEN Progress = 1 THEN 'Complete' ELSE 'Incomplete' END AS [Part Status]
,[Latest Part]
FROM Car c
LEFT JOIN
(
SELECT p.CarId
,SUM(PercentComplete)/COUNT(1) AS [Progress]
,MAX(CASE WHEN PartId = LatestPartId THEN p.Name ELSE NULL END) AS [Latest Part]
FROM Part p
LEFT JOIN (
SELECT CarId
,MAX(PartId) AS [LatestPartId]
FROM Part
GROUP BY CarId
) latestPart
ON latestPart.CarId = p.CarId
GROUP BY p.CarId
) partInfo
ON partInfo.CarId = c.CarId
关键是将GROUP BY
从主选择中删除(更喜欢)并使用GROUP BY
将我的聚合代码放入一系列JOIN中,在必要时嵌套连接以计算一些中间信息。
我觉得愚蠢,因为我没想过在SELECT
中使用JOIN
,我的大脑只想在FROM
内使用它。现在我可以有效地使用旋转和连接!