以下の結果を作成するのに助けが必要です。 SQLピボットについて考えましたが、使用方法がわかりません。いくつかの例を見て、解決策を思い付くことができません。これを達成する方法に関する他のアイデアも歓迎します。ステータス列は動的に生成される必要があります。
3つのテーブル、assets、assettypes、assetstatusがあります
テーブル:資産 assetid int assettag varchar(25) assettype int assetstatus int Table :assettypes id int typename varchar(20)(例:デスクトップ、ラップトップ、サーバーなど) テーブル:assetstatus id int statusname varchar(20)(例:展開済み、インベントリ、出荷など)
望ましい結果:
AssetTypeの展開されたインベントリの合計出荷数... -------------------------------- --------------------------- デスクトップ100 75 20 5 ... ラップトップ75 56 19 1 ... サーバー60 50 10 0 ...
一部のデータ:
資産テーブル: 1、hol1234,1,1 2、hol1233,1,2 3、hol3421,2,3 4 、svr1234,3,1 assettypesテーブル: 1、Desktop 2、Laptop 3、Server assetstatusテーブル: 1、Deployed 2、Inventory 3、Shipped
このタイプの変換はピボットと呼ばれます。使用しているデータベースを指定しなかったため、SQL ServerおよびMySQLの回答を提供します。
SQL Server:SQL Server 2005+を使用している場合は、PIVOT
関数を実装できます。
列に変換する値の数がわかっている場合は、クエリをハードコーディングできます。
select typename, total, Deployed, Inventory, shipped
from
(
select count(*) over(partition by t.typename) total,
s.statusname,
t.typename
from assets a
inner join assettypes t
on a.assettype = t.id
inner join assetstatus s
on a.assetstatus = s.id
) d
pivot
(
count(statusname)
for statusname in (Deployed, Inventory, shipped)
) piv;
SQL Fiddle with Demo を参照してください。
ただし、不明な数のstatus
値がある場合は、実行時に動的SQLを使用して列のリストを生成する必要があります。
DECLARE @cols AS NVARCHAR(MAX),
@query AS NVARCHAR(MAX)
select @cols = STUFF((SELECT distinct ',' + QUOTENAME(statusname)
from assetstatus
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set @query = 'SELECT typename, total,' + @cols + ' from
(
select count(*) over(partition by t.typename) total,
s.statusname,
t.typename
from assets a
inner join assettypes t
on a.assettype = t.id
inner join assetstatus s
on a.assetstatus = s.id
) x
pivot
(
count(statusname)
for statusname in (' + @cols + ')
) p '
execute(@query)
SQL Fiddle with Demo を参照してください
これは、case式で集約関数を使用して記述することもできます。
select typename,
total,
sum(case when statusname ='Deployed' then 1 else 0 end) Deployed,
sum(case when statusname ='Inventory' then 1 else 0 end) Inventory,
sum(case when statusname ='Shipped' then 1 else 0 end) Shipped
from
(
select count(*) over(partition by t.typename) total,
s.statusname,
t.typename
from assets a
inner join assettypes t
on a.assettype = t.id
inner join assetstatus s
on a.assetstatus = s.id
) d
group by typename, total
SQL Fiddle with Demo を参照してください
MySQL:このデータベースにはpivot関数がないため、集約関数を使用する必要があります。 CASE
式。また、ウィンドウ関数もありませんので、クエリを次のようにわずかに変更する必要があります。
select typename,
total,
sum(case when statusname ='Deployed' then 1 else 0 end) Deployed,
sum(case when statusname ='Inventory' then 1 else 0 end) Inventory,
sum(case when statusname ='Shipped' then 1 else 0 end) Shipped
from
(
select t.typename,
(select count(*)
from assets a1
where a1.assettype = t.id
group by a1.assettype) total,
s.statusname
from assets a
inner join assettypes t
on a.assettype = t.id
inner join assetstatus s
on a.assetstatus = s.id
) d
group by typename, total;
SQL Fiddle with Demo を参照してください
次に、MySQLで動的なソリューションが必要な場合、準備されたステートメントを使用して、実行するsql文字列を生成する必要があります。
SET @sql = NULL;
SELECT
GROUP_CONCAT(DISTINCT
CONCAT(
'sum(CASE WHEN statusname = ''',
statusname,
''' THEN 1 else 0 END) AS `',
statusname, '`'
)
) INTO @sql
FROM assetstatus;
SET @sql
= CONCAT('SELECT typename,
total, ', @sql, '
from
(
select t.typename,
(select count(*)
from assets a1
where a1.assettype = t.id
group by a1.assettype) total,
s.statusname
from assets a
inner join assettypes t
on a.assettype = t.id
inner join assetstatus s
on a.assetstatus = s.id
) d
group by typename, total');
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SQL Fiddle with Demo を参照してください。
結果は、両方のデータベースのすべてのクエリで同じです。
| TYPENAME | TOTAL | DEPLOYED | INVENTORY | SHIPPED |
-----------------------------------------------------
| Desktop | 2 | 1 | 1 | 0 |
| Laptop | 1 | 0 | 0 | 1 |
| Server | 1 | 1 | 0 | 0 |
ピボットに準拠していないDBMS(絶対データベース)を使用すると、次のSQLクロスタブ同等のステートメントを使用してより成功しました。
SELECT
sub.TypeName
, SUM(sub.[Count]) AS "Total"
, SUM(CASE WHEN AssetStatus='1' THEN sub.[Count] ELSE 0 END) AS "Deployed"
, SUM(CASE WHEN AssetStatus='2' THEN sub.[Count] ELSE 0 END) AS "Inventory"
, SUM(CASE WHEN AssetStatus='3' THEN sub.[Count] ELSE 0 END) AS "Shipped"
FROM
(
SELECT
t.TypeName
, AssetStatus
, COUNT(AssetID) AS "Count"
FROM
Assets
JOIN AssetTypes t ON t.ID = AssetType
JOIN AssetStatus s ON s.ID = AssetStatus
GROUP BY t.TypeName, AssetStatus, s.StatusName
) sub
GROUP BY sub.TypeName
;
このコード(上記)がMySQLで動作しないことに気づいたので、現在のAbsolute Databaseと同様にMySQLでも同様に実行できるようにコードを修正しました。その理由は、dBase、Paradox、および主流データベースでは受け入れられないCOUNT(NULL)= 0を寛大に受け入れるAbsolute Databaseの落とし穴を回避する特定のNULL処理です。だから、これはほとんどのデータベースでうまく実行されると信じて(CASE ..を処理する)これは私の適応コードです:
SELECT
sub.TypeName
, SUM(sub.AssetCase) AS "Total"
, SUM(CASE WHEN sub.StatusName = 'Deployed' THEN sub.AssetCase ELSE 0 END) AS "Deployed"
, SUM(CASE WHEN sub.StatusName = 'Inventory' THEN sub.AssetCase ELSE 0 END) AS "Inventory"
, SUM(CASE WHEN sub.StatusName = 'Shipped' THEN sub.AssetCase ELSE 0 END) AS "Shipped"
FROM
(
SELECT
c.TypeName
, c.StatusName
, CASE WHEN a.AssetID IS NULL THEN 0 ELSE 1 END AS "AssetCase"
FROM
(
SELECT
t.ID AS tID
, t.TypeName
, s.ID AS sID
, s.StatusName
FROM
AssetTypes t, AssetStatus s
) c
LEFT JOIN Assets a
ON a.AssetType = c.tID AND a.AssetStatus = c.sID
) sub
GROUP BY
sub.TypeName
;
よろしくNiels Knabe