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のは、メインの select を除外し (はるかに望ましい)、集計コードを を使用して一連の JOIN に配置しGROUP BY、必要に応じて結合をネストして中間情報を計算することでした。

SELECT内で使用することを考えていなかったので、私の脳は内JOINで使用することだけを考えたかったので、私はばかげていますFROM。これで、ピボットと連結を効果的に使用できるようになりました。

これがSQL Fiddleです。

于 2012-09-24T21:12:34.490 に答える