SQL Pivot 和 Distinct from new Columns
Posted
技术标签:
【中文标题】SQL Pivot 和 Distinct from new Columns【英文标题】:SQL Pivot and Distinct from new Columns 【发布时间】:2020-11-24 20:21:27 【问题描述】:我有一个名为 ADMIN
的表,最初看起来像这样
KEY VALUE
Version1 2019_RQK@2019
Version2 2019_RQK@2020
Version2 2019_RQK@2021
Version2 2019_RQK@2022
Version2 2020_TKA@2020
Version2 2020_TKA@2021
Version2 2020_TKA@2022
Version2 2020_TKA@2023
我正试着把它改成这样
VERSION YEAR1 YEAR2 YEAR3 YEAR4
2019_RQK 2019 2020 2021 2022
2020_TKA 2020 2021 2022 2023
我编写了一些 SQL 以获得 [VALUE]
列的左右版本,但我不知道如何压缩它,以便它只显示 DISTINCT
至 [VALUE]
列的左侧.我尝试使用 distinct,但它仍然会出现相同的重复条目,这是我到目前为止所写的,我不知道 PIVOT
函数是否可以在这里工作我尝试了一些最终没有正确的事情。
SELECT DISTINCT LEFT([VALUE], 7) AS VERSION, RIGHT([VALUE], 4) AS YEAR
FROM ADMIN
WHERE [KEY] LIKE '%VERSION%'
只是给我,不知道如何在同一个查询中更改它
VERSION YEAR
2019_RQK 2019
2019_RQK 2020
2019_RQK 2021
2019_RQK 2022
2020_TKA 2020
2020_TKA 2021
2020_TKA 2022
2020_TKA 2023
【问题讨论】:
【参考方案1】:所以,是的,您需要一个 PIVOT 表来执行此操作。您可以通过 here 了解所有关于它们的信息,其中有一个非常简单(而且快速!)的演练,以了解它为何如此神奇。
要透视此表,我们需要为 YEAR1、YEAR2 等添加一列。这样它们就可以成为我们的标题/新列。我会用一个基本的ROW_NUMBER
函数来做到这一点。我知道这个示例每个条目最多有 4 个新列,因此我将它们硬编码,但上面的链接说明了如果最大列数未知,如何动态生成 IN
语句。
请注意,我的测试表是用 col1 和 col2 创建的,因为我很懒。您应该将它们交换为实际的列名。
SELECT * FROM (
-- we start with your basic table, as you provided
SELECT
LEFT(col2, 7) AS VERSION,
RIGHT(col2, 4) AS YEAR,
ROW_NUMBER() OVER (partition by LEFT(col2, 7) order by RIGHT(col2, 4)) as YearNum /* sort these by the proper year, so we don't get of order */
FROM ADMIN
WHERE col1 LIKE '%VERSION%'
) versionResults
PIVOT (
max([YEAR]) -- grab the year
for [YearNum] -- this column holds our new column headers
in ( /* these are the possible YearNum values, now our new column headers */
[1],
[2],
[3],
[4]
)
) as pivotResults
Demo here.
【讨论】:
【参考方案2】:您还需要提取前 4 个字符作为“基准年”,然后从“基准年”中减去“年”(并加 1)以获得整数值 (1-4) 并将其用作PIVOT 列表。
Example Fiddle
这是“困难”的原因是您在 1 列中存储了 3 个键值。至少它的固定宽度很容易持续分开。
如果 VALUE 列包含不同格式的数据,这将不起作用。
CREATE TABLE Admin
( Key1 char(8)
, Val char(13)
);
INSERT INTO Admin (Key1, Val)
VALUES
('Version1','2019_RQK@2019')
, ('Version2','2019_RQK@2020')
, ('Version2','2019_RQK@2021')
, ('Version2','2019_RQK@2022')
, ('Version2','2020_TKA@2020')
, ('Version2','2020_TKA@2021')
, ('Version2','2020_TKA@2022')
, ('Version2','2020_TKA@2023');
WITH Src AS (
SELECT
Version = SUBSTRING(Val,1,8)
, Year = CAST(SUBSTRING(Val,10,4) as int)
, YearCt = CAST(SUBSTRING(Val,10,4) as int) - CAST(SUBSTRING(Val,1,4) as int) + 1
FROM Admin
)
SELECT
pvt.Version
, Year1 = pvt.[1]
, Year2 = pvt.[2]
, Year3 = pvt.[3]
, Year4 = pvt.[4]
FROM Src
PIVOT (MAX(Year) FOR YearCt IN ([1],[2],[3],[4])) pvt;
【讨论】:
您好,五个答案基本相同,但您似乎基于一些基本假设,即版本名称的年份和可能的发布年份是连续的/相隔一定数量。例如,如果我将最后一个数据行更改为2020_TKA@2026
,当我玩您的演示时,您的结果中该字段为null
。
@CoffeeNeedCoffee 不错。我确实做出了这个假设,甚至没有意识到我有(我在我的生产环境中编写了类似的查询来处理保证的顺序值,所以我冲进去了)。【参考方案3】:
就选择当前插入的年份而言,在数据更改时不对查询应用任何操作
DECLARE @cols AS NVARCHAR(MAX), @query AS NVARCHAR(MAX)
SET @cols = ( SELECT STRING_AGG(CONCAT('[year',[n],']'),',')
FROM (SELECT DISTINCT
ROW_NUMBER() OVER
(PARTITION BY LEFT([value], 7) ORDER BY [value]) AS [n]
FROM [admin] ) q );
SET @query =
N'SELECT *
FROM
(
SELECT DISTINCT LEFT([value], 7) AS version, RIGHT([value], 4) AS year,
CONCAT(''year'',ROW_NUMBER() OVER
(PARTITION BY LEFT([value], 7)
ORDER BY RIGHT([value], 4))) AS [n]
FROM [admin]
WHERE [key] LIKE ''%VERSION%'' ) q
PIVOT (
MAX([year]) FOR [n] IN (' + @cols + N')
) p
ORDER BY [version]';
EXEC sp_executesql @query;
Demo
顺便说一句,您还可以将value
拆分为@
符号SUBSTRING([value],1,CHARINDEX('@',[value])-1)
以提取version
和SUBSTRING([value],CHARINDEX('@',[value])+1,LEN([value]))
以提取year
列而不将长度值指定为函数中的参数作为替代。
【讨论】:
以上是关于SQL Pivot 和 Distinct from new Columns的主要内容,如果未能解决你的问题,请参考以下文章
SQL Pivot Table w/ and w/o Distinct Same Result
在 Snowflake 中使用 Count Distinct 和 Pivot