Как транспонировать строки в столбцы с указанными именами столбцов? - PullRequest
0 голосов
/ 12 марта 2020

Я хотел бы создать запрос для сопоставления продуктов и связанных с ними изображений в соответствии со схемой целевой базы данных. В моей исходной базе данных хранятся URL-адреса SEO для каждого продукта в таблице Picture с использованием Product_Picture_Mapping в качестве таблицы двустороннего сопоставления. Таким образом, каждая таблица Product может иметь от 0 до n изображений, связанных с ней.

SELECT 
    Name, Price, SeoFilename
FROM Product prod
    JOIN Product_Picture_Mapping map
    ON prod.Id = map.ProductId 
    JOIN Picture pict
    ON pict.Id = map.PictureId  


Name            Price   SeoFilename
-----------------------------------
Strawberries    11      strawberry
Strawberries    11      strawberry_1
Pineapples      10      pineapples
Banana          10      banana
Banana          10      banana_1
Orange          11      orange

К сожалению, таблица продуктов целевой базы данных содержит от 0 до 3 URL-адресов SEO, которые хранятся в виде полей. Это делает написание запроса довольно сложным, так как мне нужно было бы транспонировать строки источника в именованные столбцы, например:

Name            Price   MainImageUrl    OtherImageUrl1  OtherImageUrl2
Banana          10      banana          banana_1        null
Orange          11      orange          null            null
Pineapples      10      pineapples      null            null    
Strawberries    11      Strawberry      Strawberry_1    null

Я пытался использовать рекомендованную функцию PIVOT, но он может генерировать только агрегированные значения, так как требует агрегатной функции. Я видел другие методы, но обычно они представляют собой одиночные соединения с подзапросами.

Вот ссылка на мою скрипку БД: https://dbfiddle.uk/?rdbms=sqlserver_2019&fiddle=786b419936007c85f7f71f0defe5b829

Ответы [ 3 ]

0 голосов
/ 12 марта 2020

Я бы предложил пользователю функцию Pivot вместе с ROW_NUMBER () OVER (раздел по имени, цена ORDER BY SeoFilename)

With temp as (
SELECT 
    Name,
    Price, 
    SeoFilename,
    ROW_NUMBER() OVER (partition by Name, Price ORDER BY SeoFilename) as seqnum

FROM 
    Product prod
    JOIN Product_Picture_Mapping map
    ON prod.Id = map.ProductId 
    JOIN Picture pict
    ON pict.Id = map.PictureId
)
select
    Name,
    price,
    "1" as MainImageUrl,
    "2" as OtherImageUrl1,
    "3" as OtherImageUrl2,
    "4" as OtherImageUrl3,
    "5" as OtherImageUrl4
 from
        Temp
    pivot
    (
         min(SeoFilename)
         for seqnum in ( 1,2,3,4,5 ) -- Depends on how deep is it
    );
  • "ROW_NUMBER () OVER (раздел по Имя, Цена ORDER BY SeoFilename) "создал порядковый номер (" 1 "," 2 "," 3 "и т. Д. c) для каждой строки с разными именем и ценой.

  • Функция Pivot будет транспонировать результат по имени и цене на основе порядкового номера, указанного в столбце seqnum.

  • Фактически минимальная функция агрегирования ничего не делать, так как для каждого seqnum будет только одно имя SeoFile с тем же именем и ценой. Это было только для того, чтобы полностью выполнить требование функции pivot.
0 голосов
/ 12 марта 2020

Уловка двоякая. Сначала вам нужно добавить способ разделения данных по имени. Rownumber поможет в этом. Во-вторых, нужно повернуть старомодный путь.

Rownumber - это оконная функция, и вы можете добавить к ней раздел. этот раздел необходим, чтобы определить, будет ли что-то первым, вторым, третьим транспонированным столбцом. Единственным недостатком этого метода является необходимость жестко задавать количество транспонированных столбцов

. Старомодные сводные диаграммы работают с утверждениями group by и case. Чтобы обойти требуемую агрегацию, вы можете использовать оператор MAX, так как в этом случае в основном игнорируется оператор max.

if OBJECT_ID('tempdb..#test') is not null
begin
    drop table #test
end

create table #test
(
name varchar(50)
,price int
,seofilename varchar(50)
)

insert into #test
values 
('apple', 10, 'apple1'),
('apple',10,'apple4'),
('pear',23,'pear1'),
('pear',23,'pear5'),
('banana',56,'banan9'),
('banana',56,'banan6'),
('banana',56,'banan')

select
    name,
    price,
    max(case when rownum = 1 then seofilename else null end) as PrimaryFileName,
    max(case when rownum = 2 then seofilename else null end) as SecondaryFileName,
    max(case when rownum = 3 then seofilename else null end) as TertiaryFileName
from
(
select 
    name, 
    price, 
    seofilename,
    ROW_NUMBER() over (partition by name order by name) as rownum 
from 
    #test
) Q
group by
    name,
    price
0 голосов
/ 12 марта 2020

Я думаю, что вы хотите условное агрегирование:

SELECT Name, Price,
       MAX(CASE WHEN seqnum = 1 THEN SeoFilename END) as SeoFilename_1,
       MAX(CASE WHEN seqnum = 2 THEN SeoFilename END) as SeoFilename_2,
       MAX(CASE WHEN seqnum = 3 THEN SeoFilename END) as SeoFilename_3,
       MAX(CASE WHEN seqnum = 4 THEN SeoFilename END) as SeoFilename_4
FROM (SELECT Name, Price, SeoFilename,
             ROW_NUMBER() OVER (ORDER BY name, price SeoFilename) as seqnum
      FROM Product p JOIN
           Product_Picture_Mapping ppm
           ON p.Id = ppm.ProductId JOIN
           Picture pic
           ON pic.Id = ppm.PictureId  
     ) pi
GROUP BY Name, Price;

Это не дает точного набора результатов в вашем вопросе. Но я думаю, что это то, что вы хотите.

Здесь - это дб <> скрипка.

...