SQL Server: выберите различное количество значений на основе значения столбца

Версия: Microsoft SQL Server 2014 — 12.0.2000.8 (X64) 20 февраля 2014 г. 20:04:26 Авторские права (c) Microsoft Corporation Express Edition (64-разрядная версия) для Windows NT 6.1 (сборка 7601: пакет обновления 1)

Мне нужно выбрать различное количество значений из таблицы, где определенный столбец равен параметру, а определенный столбец похож на «String1» или «String2».

Я создал хранимую процедуру, которая возвращает строки MAX и MIN, но, естественно, этот метод не является динамическим.

Я пробовал следующий запрос, который говорит, что он успешно завершен, но не возвращает никаких результатов.

SELECT UPC, PartNum, PartDesc
FROM dbo.table
WHERE UPC = @upc
GROUP BY UPC, PartNum, PartDesc
HAVING PartDesc in ('%RED%','%BLUE%')
ORDER BY PartDesc; 

Пример таблицы:

ID          UPC       PartNum      PartDesc
-------------------------------------------
1           123        543         Red1
2           123        345         Blue1
3           123        654         Red2
4           123        765         Blue2

Мне нужно передать параметр хранимой процедуре как @upc из приложения.

Где он найдет любой PartDesc, похожий на «% RED%» или «% BLUE%» И где UPC = @upc. Затем сохраните найденные номера частей в новой таблице для последующего запроса.

Созданная таблица из хранимой процедуры:

ID    UPC    Red1    Red2    Blue1    Blue2
----------------------------------------------------------
1     123    543     654     345      765

Для каждого номера UPC может быть любое количество или комбинация «красного» или «синего». т. е. некоторые номера UPC могут иметь только две «красные» части и одну «синюю» часть, а другие могут иметь только две «красные» части и не иметь «синих» частей. Может быть, пять «красных» частей и десять «синих».

Как мне написать запрос, который будет хранить различное количество найденных результатов в новой таблице в хранимой процедуре?

Изменить Кажется, что должна использоваться функция PIVOT, но я не знаю, как использовать требуемый агрегат в моем сценарии. Мне не нужно ориентироваться на «СУММУ» PartDesc или любого другого столбца в этом отношении. Возможно, динамический Pivot?

ИЗМЕНИТЬ На основе рекомендации Корги. Также показываю свои работы.

DECLARE @upc As varchar(13)
DECLARE @Red1 As nvarchar(100) =    CASE
                                        WHEN
                                                (
                                                    SELECT MIN(PartNum) FROM dbo.table
                                                    WHERE PartDesc LIKE '%RED%' AND UPC = @upc
                                                ) IS NOT NULL THEN

                                                (                                               
                                                    SELECT MIN(PartNum) FROM dbo.table
                                                    WHERE PartDesc LIKE '%RED%' AND UPC = @upc
                                                ) 

                                        ELSE 'Not Found'
                                     END

DECLARE @Red2 As nvarchar(100) =    CASE
                                        WHEN
                                                (
                                                    SELECT MAX(PartNum) FROM dbo.table
                                                    WHERE PartDesc LIKE '%RED%' AND UPC = @upc
                                                ) IS NOT NULL THEN

                                                (                                               
                                                    SELECT MAX(PartNum) FROM dbo.table
                                                    WHERE PartDesc LIKE '%RED%' AND UPC = @upc
                                                ) 

                                        ELSE 'Not Found'
                                     END

DECLARE @Blue1 As nvarchar(100) =   CASE
                                        WHEN
                                                (
                                                    SELECT MAX(PartNum) FROM dbo.table
                                                    WHERE PartDesc LIKE '%BLUE%' AND UPC = @upc
                                                ) IS NOT NULL THEN

                                                (                                               
                                                    SELECT MAX(PartNum) FROM dbo.table
                                                    WHERE PartDesc LIKE '%BLUE%' AND UPC = @upc
                                                ) 

                                        ELSE 'Not Found'
                                     END

;WITH MostColumns AS
(
    SELECT UPC, @Red1 As Part1, @Red2 As Part2, @Blue1 As Part3
    FROM   (SELECT UPC, PartNum, PartDesc
            FROM   dbo.table) AS source
           PIVOT
           (MIN(PartNum) FOR PartDesc IN ([Part1], [Part2], [Part3])) AS pvt
)
SELECT MIN(p.ID) AS ID, p.UPC, mc.Part1, mc.Part2, mc.Part3
INTO   MyNewTable
FROM   dbo.table p
INNER JOIN MostColumns mc ON p.UPC = mc.UPC
GROUP BY p.UPC, mc.Part1, mc.Part2, mc.Part3

Результат:

ID      UPC       Part1           Part2            Part3
2876    123      Not Found      Not Found         Not Found
2758    213      Not Found      Not Found         Not Found
2321    312      Not Found      Not Found         Not Found
802     321      Not Found      Not Found         Not Found
868     132      Not Found      Not Found         Not Found

Это правильный формат, но не сигара. Я точно знаю, что все мои БЗК содержат как минимум одну Red1 часть. Почему-то не нашел ни одной части.

EDIT--ANSWER @Corgi После дополнительных исследований динамических поворотов я пришел к этому решению. Мне все еще нужно будет доработать его, чтобы заставить его работать так, как мне нужно. Хотя, они не имеют отношения к этому вопросу. Спасибо @bluefeet за ваш ответ в этом посте. Динамический свод SQL

DECLARE @cols AS NVARCHAR(MAX),
    @query  AS NVARCHAR(MAX)

select @cols = STUFF((SELECT distinct ',' 
                        + QUOTENAME('Part_' + cast(rn as varchar(10))) 
                    from dbo.table
                    cross apply
                    (
                      select row_number() over(partition by UPC order by PartNum) rn
                      from dbo.table
                    ) x
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)') 
        ,1,1,'')

set @query = 'SELECT UPC, ' + @cols + ' from 
             (
                select UPC, PartNum,
                  ''Component_''
                    + cast(row_number() over(partition by UPC order by PartNum) as varchar(10)) val
                from dbo.table
            ) x
            pivot 
            (
                max(PartNum)
                for val in (' + @cols + ')
            ) p '

execute(@query)

person Seth Johnson    schedule 04.05.2017    source источник


Ответы (1)


Ваше замечание о PIVOT соответствует действительности, но вы не можете использовать PIVOT без указания имен выходных столбцов (то есть значений из PartDesc). Похоже, поскольку может быть разное количество этих PartDesc значений, самое близкое, что вы можете получить, это найти все значения:

SELECT DISTINCT t.PartDesc
FROM   MyTable t
WHERE  t.PartDesc LIKE '%Red%' OR t.PartDesc LIKE '%Blue%'

Затем вы можете использовать значения для построения запроса. Если вам действительно нужно сделать запрос динамическим, вам потребуется создать строку запроса для использования с чем-то вроде sp_executesql. Вы можете создать таблицу из выходных данных с помощью SELECT... INTO в вашем динамическом запросе.

PIVOT синтаксис, который вам нужен, в сочетании с SELECT... INTO может выглядеть примерно так:

;WITH MostColumns AS
(
    SELECT UPC, Red1, Red2, Blue1, Blue2
    FROM   (SELECT UPC, PartNum, PartDesc
            FROM   dbo.table) AS source
           PIVOT
           (MIN(PartNum) FOR PartDesc IN ([Red1], [Red2], [Blue1], [Blue2])) AS pvt
)
SELECT MIN(p.ID) AS ID, p.UPC, mc.Red1, mc.Red2, mc.Blue1, mc.Blue2
INTO   MyNewTable
FROM   dbo.table p
INNER JOIN MostColumns mc ON p.UPC = mc.UPC
GROUP BY p.UPC, mc.Red1, mc.Red2, mc.Blue1, mc.Blue2

Общее табличное выражение MostColumns существует потому, что ID в исходном запросе не очень хорошо работает — это «дополнительный» столбец, который не является частью сводной таблицы.

person Andrew    schedule 04.05.2017
comment
Спасибо Корги. Ваш последний пример кажется, что он будет работать, если мои значения статичны, не так ли? Я поиграл с ним, и результат — это то, что мне нужно, однако мне нужен только один номер «UPC», а затем перечислить все доступные «красные» и «синие» части. Я буду передавать параметр '@upc' с запрошенным 'UPC' из другого приложения. Затем он должен использовать параметр «@upc», чтобы затем найти любые/все «красные» и «синие» части, а затем обновить другую таблицу с результатом вместе с параметром «@upc». Я постоянно сталкиваюсь с ошибкой «Подзапрос вернул слишком много результатов», но мне действительно нужны эти результаты. - person Seth Johnson; 05.05.2017
comment
@SethJohnson Вы правы, говоря, что последний пример должен работать, если значения являются статическими. Единственный реальный способ обработки динамических значений — объединить запрос как переменную NVARCHAR(MAX), а затем запустить EXEC sp_executesql с запросом в качестве параметра. Объединение строк для создания запроса имеет последствия для безопасности (внедрение SQL), поэтому убедитесь, что вы правильно заключаете в кавычки все имена столбцов с помощью QUOTENAME. Кстати, COALESCE может исключить некоторые из этих CASE утверждений. - person Andrew; 05.05.2017