我正在处理旧版数据库,需要开发一个SQL查询以提供给客户。作为遗留数据库,它在设计时就没有考虑这种类型的查询。我简化了我需要选择的两个表,以使示例更容易理解。我有一个“长桌”,需要使其“变宽”。我尝试使用PIVOT
,但是遇到两个问题:
我需要一个SQL查询,该查询将针对以下给定架构输出如下结果:
| [Id] | [Author] | [PublishedYear] | [Title] |
-------------------------------------------------
| 1 | 'Robert' | '2017' | null |
| 2 | 'Tim' | null | null |
| 3 | null | '2018' | null |
| 4 | null | null | 'Winning' |
要构建的SQL示例:
CREATE TABLE [Book] (
[Id] int
);
INSERT INTO [Book] ([Id])
VALUES (1);
INSERT INTO [Book] ([Id])
VALUES (2);
INSERT INTO [Book] ([Id])
VALUES (3);
INSERT INTO [Book] ([Id])
VALUES (4);
CREATE TABLE [BookProperty] (
[Name] VARCHAR(100),
[Value] VARCHAR(100),
[BookId] int
);
INSERT INTO [BookProperty] ([Name], [Value], [bookId])
VALUES ('Author', 'Robert', 1);
INSERT INTO [BookProperty] ([Name], [Value], [bookId])
VALUES ('Author', 'Tim', 2);
INSERT INTO [BookProperty] ([Name], [Value], [bookId])
VALUES ('PublishedYear', '2018', 3);
INSERT INTO [BookProperty] ([Name], [Value], [bookId])
VALUES ('PublishedYear', '2017', 1);
INSERT INTO [BookProperty] ([Name], [Value], [bookId])
VALUES ('Title', 'Winning', 4);
答案 0 :(得分:2)
您可以尝试使用条件汇总函数,MAX
和CASE WHEN
和GROUP BY
CREATE TABLE [Book] (
[Id] int
);
INSERT INTO [Book] ([Id])
VALUES (1);
INSERT INTO [Book] ([Id])
VALUES (2);
INSERT INTO [Book] ([Id])
VALUES (3);
INSERT INTO [Book] ([Id])
VALUES (4);
CREATE TABLE [BookProperty] (
[Name] VARCHAR(100),
[Value] VARCHAR(100),
[BookId] int
);
INSERT INTO [BookProperty] ([Name], [Value], [bookId])
VALUES ('Author', 'Robert', 1);
INSERT INTO [BookProperty] ([Name], [Value], [bookId])
VALUES ('Author', 'Tim', 2);
INSERT INTO [BookProperty] ([Name], [Value], [bookId])
VALUES ('PublishedYear', '2018', 3);
INSERT INTO [BookProperty] ([Name], [Value], [bookId])
VALUES ('PublishedYear', '2017', 1);
INSERT INTO [BookProperty] ([Name], [Value], [bookId])
VALUES ('Title', 'Winning', 4);
查询1 :
SELECT id,
MAX(CASE WHEN Name = 'Author' THEN Value END) as 'Author',
MAX(CASE WHEN Name = 'PublishedYear' THEN Value END) as 'PublishedYear',
MAX(CASE WHEN Name = 'Title' THEN Value END) as 'Title'
FROM [Book] b INNER JOIN [BookProperty] bp
on b.Id = bp.BookId
GROUP BY id
Results :
| id | Author | PublishedYear | Title |
|----|--------|---------------|---------|
| 1 | Robert | 2017 | (null) |
| 2 | Tim | (null) | (null) |
| 3 | (null) | 2018 | (null) |
| 4 | (null) | (null) | Winning |
编辑
您可以尝试使用动态枢纽来达到期望。
使用STUFF
函数动态创建条件聚合函数 execute语句,然后使用execute动态执行您的sql。
DECLARE @cols AS NVARCHAR(MAX),
@query AS NVARCHAR(MAX);
SET @cols = STUFF((SELECT DISTINCT ',MAX(CASE WHEN Name = ''' + Name +''' THEN [Value] END) as ''' + Name + ''' '
FROM [Book] b INNER JOIN [BookProperty] bp
on b.Id = bp.BookId
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set @query = 'SELECT ID, '+ @cols + ' FROM [Book] b INNER JOIN [BookProperty] bp on b.Id = bp.BookId GROUP BY id'
execute(@query)
答案 1 :(得分:0)
面临的挑战是您拥有一个EAV,并试图调整价值。这是您必须使用的设计中的丑陋事物之一。您将需要动态sql。我仍将使用条件聚合(又称为交叉表),但您需要制作一个动态版本。这是一个例子,与我几年前写的内容并没有太大不同。您应该可以很容易地将其调整到您的数据集。如果您需要帮助修改此内容,请告诉我,我会帮忙。
if OBJECT_ID('tempdb..#Something') is not null
drop table #Something
if OBJECT_ID('tempdb..#ColumnNames') is not null
drop table #ColumnNames
create table #Something
(
REQUEST_ID int
, ITEM_ID int
, ErrorType varchar(50)
)
insert #Something values
(6019, 5054257, 'Under construction')
, (6024, 5054712, 'KSCV417W')
, (6024, 5054713, 'Under construction')
, (6024, 5054715, 'Under construction')
, (6029, 5164288, 'KSAC680E')
, (6029, 5164289, 'KSAC680E')
, (6029, 5164290, 'KSAC680E')
, (6029, 5164292, 'KSAC680E')
create table #ColumnNames --we need some way to get the Column Names in a table so we can join to this to generate the dynamic columns.
(
ColNum int identity
, ColName sysname
)
insert #ColumnNames
select distinct ErrorType
from #Something
order by ErrorType desc
declare @StaticPortion nvarchar(2000) =
'with OrderedResults as
(
select REQUEST_ID
, ErrorType
, GroupCount = count(*)
, ColNum
from #Something s
join #ColumnNames cn on cn.ColName = s.ErrorType
group by REQUEST_ID
, ErrorType
, ColNum
)
select REQUEST_ID';
declare @DynamicPortion nvarchar(max) = '';
with E1(N) AS (select 1 from (values (1),(1),(1),(1),(1),(1),(1),(1),(1),(1))dt(n)),
E2(N) AS (SELECT 1 FROM E1 a, E1 b), --10E+2 or 100 rows
E4(N) AS (SELECT 1 FROM E2 a, E2 b), --10E+4 or 10,000 rows max
cteTally(N) AS
(
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E4
)
select @DynamicPortion = @DynamicPortion +
', MAX(Case when ColNum = ' + CAST(N as varchar(6)) + ' then convert(varchar(4), GroupCount) end) as [' + cn.ColName + ']'
from cteTally t
join #ColumnNames cn on cn.ColNum = t.N
where t.N <=
(
select top 1 Count(*)
from #Something
group by REQUEST_ID
order by COUNT(*) desc
)
declare @FinalStaticPortion nvarchar(2000) = ' from OrderedResults Group by REQUEST_ID order by REQUEST_ID';
declare @SqlToExecute nvarchar(max) = @StaticPortion + @DynamicPortion + @FinalStaticPortion;
--select @SqlToExecute --You can use this to help debug the dynamic sql
exec sp_executesql @SqlToExecute