Перенести неопределенное количество строк в столбцы?

У меня есть две таблицы с данными, назовем их таблицей T и таблицей B. Соответствующие данные в T и B имеют отношение «многие к одному» друг с другом, например:

T.idB.код
А1
А1,2
Б1,2
Б1,5
С1
С2
С3
С15
Д296

и т. д. (это всего лишь грубая демонстрация и не похожа на фактические данные, которые я использую).

Я выполнил запрос count(*) и обнаружил, что наибольшее количество элементов из таблицы T появляется в таблице B равно 8. Я хочу транспонировать приведенную выше таблицу, которая имеет тысячи и тысячи записей в T и несколько сотни возможных записей для соответствующего значения B в таблице из 9 столбцов, например:

T.idКОД 1КОД 2КОД 3КОД 4КОД 5КОД 6КОД 7КОД 8
А11,2НУЛЕВОЙНУЛЕВОЙНУЛЕВОЙНУЛЕВОЙНУЛЕВОЙНУЛЕВОЙ
Б1,21,5НУЛЕВОЙНУЛЕВОЙНУЛЕВОЙНУЛЕВОЙНУЛЕВОЙНУЛЕВОЙ
С12315НУЛЕВОЙНУЛЕВОЙНУЛЕВОЙНУЛЕВОЙ
Д296НУЛЕВОЙНУЛЕВОЙНУЛЕВОЙНУЛЕВОЙНУЛЕВОЙНУЛЕВОЙНУЛЕВОЙ

и т. д.

Единственная информация о поиске, которую я могу найти, требует либо ручного присоединения B каждый раз и исключения предыдущих значений B.code (как я сейчас это делаю), либо использования таблицы PIVOT (динамический SQL или иначе), которая обязательно будет иметь один столбец для каждого возможного значения B.code (которое, как уже упоминалось, составляет несколько сотен значений), ни одно из которых не является масштабируемым.

Для справки, мой код сейчас выглядит примерно так:

SELECT
T.id,
b1.code as 'CODE 1', b2.code as 'CODE 2', b3.code as 'CODE 3', b4.code as 'CODE 4' (etc)
FROM T
LEFT JOIN B b1 ON b1.code = 
  (SELECT TOP 1 code FROM B WHERE B.t_id = T.id)
LEFT JOIN B b2 ON b2.code = 
  (SELECT TOP 1 code FROM B WHERE B.t_id = T.id AND B.code NOT IN (b1.code))
LEFT JOIN B b3 ON b3.code = 
  (SELECT TOP 1 code FROM B WHERE B.t_id = T.id AND B.code NOT IN (b1.code, b2.code))
LEFT JOIN B b4 ON b4.code = 
  (SELECT TOP 1 code FROM B WHERE B.t_id = T.id AND B.code NOT IN (b1.code, b2.code, b3.code))

Каковы мои варианты? Я обречен? Должен быть лучший способ, верно?

По какой-то причине таблицы не отображаются здесь должным образом, но они отображаются, когда я нажимаю «Изменить». Как я могу это исправить? ? Обновлено: Спасибо, Аарон!

Gabriel Benamy 17.03.2022 17:41

Вам нужен правильный CR/LF между началом/концом уценки таблицы и другим текстом, который не является частью таблицы. etc. пытался стать частью таблицы, но структура не совпадала, поэтому бум.

Aaron Bertrand 17.03.2022 17:44

Пожалуйста, предоставьте определение таблицы для B и несколько строк примера данных. Ваш код выглядит так же, как и динамический поворот, но более сложным способом. Есть ли коды без значений?

Kendle 17.03.2022 17:45

Таблица T вообще актуальна? Если тот же код/ключ присутствует в B, нужно ли вам присоединяться? Или вы можете присоединиться к после, к которому вы изменились?

Aaron Bertrand 17.03.2022 17:47

@ Kendle Я не уверен, насколько это актуально; единственные столбцы, на которые я когда-либо ссылался в таблице B, — это два, перечисленные в первой таблице примера (хотя вместо T.id это был бы B.t_id). Но есть еще два столбца: Seq (который является собственным столбцом с уникальным идентификатором) и Lead (который равен либо 1, либо NULL). @ AaronBertrand Я могу присоединиться после поворота, так как это не имеет прямого отношения к самому повороту. Я получаю оттуда данные в виде открытого текста вместо уникального идентификатора.

Gabriel Benamy 17.03.2022 17:52

И является ли Code числом, строкой или чем-то еще? Как вы определяете, что 1 принадлежит Code 1, а 1.2 принадлежит Code 2?

Aaron Bertrand 17.03.2022 17:55
Code — это строка. Коды заменяются по порядку и не имеют приоритета, поэтому, хотя они отсортированы внутри в таблице B, мне этого достаточно.
Gabriel Benamy 17.03.2022 17:57
ReactJs | Supabase | Добавление данных в базу данных
ReactJs | Supabase | Добавление данных в базу данных
Это и есть ваш редактор таблиц в supabase.👇
Понимание Python и переход к SQL
Понимание Python и переход к SQL
Перед нами лабораторная работа по BloodOath:
2
7
47
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Не проверено, но, возможно, это поможет

Select *
 From (
        Select T.id
              ,B.Code
              ,Col = concat('Code ',row_number() over (partition by T.id order by B.code) )
         From T
         Join B on B.ID=T.ID
      ) src
 Pivot (max(Code) for Col in ([Code 1],
                              [Code 2],
                              [Code 3],
                              [Code 4],
                              [Code 5],
                              [Code 6],
                              [Code 7],
                              [Code 8]) ) pvt

@GabrielBenamy Всегда рад помочь

John Cappelletti 17.03.2022 18:04

@GabrielBenamy Должен отметить: если есть дублирующиеся коды, и вы хотите показать только разные значения (скажем, у A есть два кода = 1), используйте плотно_rank() вместо row_number()

John Cappelletti 17.03.2022 18:09

Запрос, который вы хотите использовать для завершить:

;WITH x AS 
 (
   SELECT [T.id], Code, rn = ROW_NUMBER() OVER 
     (PARTITION BY [T.id] ORDER BY @@SPID)
   FROM dbo.B
 ) SELECT [T.id], [CODE 1] = p.[1],[CODE 2] = p.[2], ... 
   FROM x
   PIVOT (MAX(Code) FOR rn IN 
   ([1],[2],...)) AS p;

Но вам нужен динамический SQL, чтобы добраться туда без необходимости жестко кодировать ...Code 8 или добавлять Code 9, когда в таблицу добавляется другой код:

DECLARE @sql     nvarchar(max) = N'',
  @outputColumns nvarchar(max),
  @pivotColumns  nvarchar(max),
  @maxCount      int = (SELECT MAX(c) FROM 
    (SELECT COUNT(*) FROM dbo.B GROUP BY [T.id]) AS c(c));

;WITH x(n) AS (SELECT 1 UNION ALL SELECT n+1
  FROM x WHERE n < @maxCount)
SELECT @outputColumns = STRING_AGG(CONCAT('[CODE ',n,
                        '] = p.',QUOTENAME(n)), ','),
       @pivotColumns  = STRING_AGG(QUOTENAME(n), N',')
 FROM x;
 
 SELECT @sql = N';WITH x AS 
 (
   SELECT [T.id], Code, rn = ROW_NUMBER() OVER 
    (PARTITION BY [T.id] ORDER BY @@SPID)
   FROM dbo.B
 ) SELECT [T.id], ' + @outputColumns + N' 
   FROM x
   PIVOT (MAX(Code) FOR rn IN 
   (' + @pivotColumns + N')) AS p;';
 
 EXEC sys.sp_executesql @sql;

Вывод (из примера данных в вопросе):

T.idCODE 1CODE 2CODE 3CODE 4
A11.2nullnull
B1.21.5nullnull
C12315
D296nullnullnull

Другие вопросы по теме