Как использовать два параметра для одного столбца в хранимой процедуре - PullRequest
0 голосов
/ 24 апреля 2020

Я попытался найти похожий вопрос о переполнении стека без успеха ...

Вот мой код хранимой процедуры:

USE [DATABASENAME]
GO

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO

ALTER PROCEDURE [dbo].[SPNAME]
  @IN_dtmin datetime, 
  @IN_dtmax datetime = null, 
  @IN_key varchar(500), 
  @IN_set varchar(80), 
  @IN_locktype varchar(500)
WITH EXEC AS CALLER
AS
BEGIN
      -- SET NOCOUNT ON added to prevent extra result sets from
      -- interfering with SELECT statements.
      SET NOCOUNT ON;

    -- Insert statements for procedure here
SELECT IDLOCK, DT, LOCKTYPE,
       MAX(CASE WHEN PRODUCTATTRIBUTE = 'CodeSet' THEN  VALUE END) AS CodeSet,
       MAX(CASE WHEN PRODUCTATTRIBUTE = 'KeySet' THEN  VALUE END) AS KeySet
FROM LOCKREGISTER LR LEFT JOIN 
     LOCKTYPES T
     ON LR.IDLOCKTYPE = T.IDLOCKTYPE LEFT JOIN 
     PRODUCTATTRIBUTES PA
     ON LR.IDPRODUCT = PA.IDPRODUCT AND 
        PRODUCTATTRIBUTE IN ('CodeSet','KeySet')
WHERE LR.DT BETWEEN IIF(@IN_DTMIN IS NULL,GETDATE(),@IN_DTMIN) AND IIF(@IN_DTMAX IS NULL,GETDATE(),@IN_DTMAX)
  AND (PA.VALUE like ISNULL(@IN_key,'%') OR (PA.VALUE IS NULL AND @IN_key IS NULL)) 
  AND (PA.VALUE like ISNULL(@IN_set,'%') OR (PA.VALUE IS NULL AND @IN_set IS NULL))
  AND (T.LOCKTYPE like ISNULL(@IN_locktype,'%') OR (T.LOCKTYPE IS NULL AND @IN_locktype IS NULL))
GROUP BY IDLOCK, DT, LOCKTYPE;
END

Это результат, если я не применяю любые фильтры:

enter image description here

И это результат, если я выполню хранимую процедуру с dates filter и фильтром Set:

enter image description here

Как вы можете видеть на изображении выше, в столбце CodeSet результаты становятся 'NULL'.

Как сохранить значения Столбца CodeSet при выполнении SP? (В некоторых случаях некоторые фильтры могут быть нулевыми).

Это пример желаемого результата для первой строки:

enter image description here

Ответы [ 2 ]

1 голос
/ 24 апреля 2020

Я считаю, что проблема в том, что PRODUCTATTRIBUTES является таблицей значений атрибутов сущности (EAV), с которой, как известно, трудно справляться в запросах.

Обратите внимание, что при втором выполнении запроса, который включает параметры, значение, введенное для @IN_set, отображается в столбце KeySet, поскольку вы не различаете значения PRODUCTATTRIBUTE (CodeSet против KeySet) в вашем предложении WHERE.

Чтобы упростить логику c в подобных запросах, я обычно несколько раз присоединяюсь к таблице EAV, создавая подзапросы, которые содержат только пары ключ / значение, которые мне нужны для каждого атрибута. Итак, в этом случае я бы присоединился к PRODUCTATTRIBUTES дважды, один раз, чтобы получить интересующие значения CodeSet, затем снова, чтобы получить значения KeySet. Я также перенесу агрегацию на эти подзапросы, что устраняет неоднозначность, а также уменьшает объем данных, загружаемых в память.

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

SELECT IDLOCK, DT, LOCKTYPE,
       CS.CodeSet,
       KS.KeySet
FROM LOCKREGISTER LR LEFT JOIN 
     LOCKTYPES T
     ON LR.IDLOCKTYPE = T.IDLOCKTYPE LEFT JOIN 
     ( -- Sub-query to get 'CodeSet' values by IDPRODUCT
      SELECT 
        IDPRODUCT,
        MAX(VALUE) AS CodeSet
      FROM PRODUCTATTRIBUTES
      WHERE PRODUCTATTRIBUTE = 'CodeSet'
      GROUP BY IDPRODUCT
     ) AS CS
     ON LR.IDPRODUCT = CS.IDPRODUCT LEFT JOIN
     (  -- Sub-query to get 'KeySet' values by IDPRODUCT
      SELECT 
        IDPRODUCT,
        MAX(VALUE) AS KeySet
      FROM PRODUCTATTRIBUTES
      WHERE PRODUCTATTRIBUTE = 'KeySet'
      GROUP BY IDPRODUCT
     ) AS KS
     ON LR.IDPRODUCT = KS.IDPRODUCT

WHERE LR.DT BETWEEN IIF(@IN_DTMIN IS NULL,GETDATE(),@IN_DTMIN) AND IIF(@IN_DTMAX IS NULL,GETDATE(),@IN_DTMAX)
  AND (KS.KeySet like ISNULL(@IN_key,'%') OR (KS.KeySet IS NULL AND @IN_key IS NULL)) 
  AND (CS.CodeSet like ISNULL(@IN_set,'%') OR (CS.CodeSet IS NULL AND @IN_set IS NULL))
  AND (T.LOCKTYPE like ISNULL(@IN_locktype,'%') OR (T.LOCKTYPE IS NULL AND @IN_locktype IS NULL));
0 голосов
/ 24 апреля 2020

Что вы поняли из вашего кода, так это то, что вам просто нужна функция ISNULL. Поэтому обновите ваш запрос SELECT до -

SELECT IDLOCK, DT, LOCKTYPE,
       ISNULL(MAX(CASE WHEN PRODUCTATTRIBUTE = 'CodeSet' THEN  VALUE END), @IN_set) AS CodeSet,
       ISNULL(MAX(CASE WHEN PRODUCTATTRIBUTE = 'KeySet' THEN  VALUE END), @IN_set) AS KeySet
FROM LOCKREGISTER LR LEFT JOIN 
     LOCKTYPES T
     ON LR.IDLOCKTYPE = T.IDLOCKTYPE LEFT JOIN 
     PRODUCTATTRIBUTES PA
     ON LR.IDPRODUCT = PA.IDPRODUCT AND 
        PRODUCTATTRIBUTE IN ('CodeSet','KeySet')
WHERE LR.DT BETWEEN IIF(@IN_DTMIN IS NULL,GETDATE(),@IN_DTMIN) AND IIF(@IN_DTMAX IS NULL,GETDATE(),@IN_DTMAX)
  AND (PA.VALUE like ISNULL(@IN_key,'%') OR (PA.VALUE IS NULL AND @IN_key IS NULL)) 
  AND (PA.VALUE like ISNULL(@IN_set,'%') OR (PA.VALUE IS NULL AND @IN_set IS NULL))
  AND (T.LOCKTYPE like ISNULL(@IN_locktype,'%') OR (T.LOCKTYPE IS NULL AND @IN_locktype IS NULL))
GROUP BY IDLOCK, DT, LOCKTYPE;
...