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')

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但它需要跨越多个列,尽管我意识到这也是可能的,CASE似乎更容易,而且我被告知更快。)

我完全不正确和糟糕的尝试:

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.

4

3 回答 3

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

SQL Fiddle with Demo

于 2012-09-24T02:29:58.497 回答
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
于 2012-09-24T02:41:53.603 回答
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主选择(非常首选)并将我的聚合代码放在一系列使用 JOIN 中GROUP BY,在必要时嵌套一个连接以计算一些中间信息。

我觉得自己很傻,没想到用SELECTinside JOIN,脑子里只想着用inside FROM。现在我可以有效地使用旋转和连接!

这是SQL Fiddle

于 2012-09-24T21:12:34.490 回答