Более эффективная альтернатива двойному соединению - PullRequest
3 голосов
/ 29 сентября 2010

У меня есть процедура с (немного более сложной) версией ниже:

CREATE PROC sp_Find_ID (
    @Match1 varchar(10),
    @Match2 varchar(10)
) AS

DECLARE @ID int

SELECT @ID = ID
FROM Table1
WHERE Match1 = @Match1
    AND Coalesce(Match2,@Match2,'') = Coalesce(@Match2,Match2,'')

SELECT @ID ID

По сути, Match1 является обязательным соответствием, но Match2 является необязательным как для ввода в процедуру, так и длястол ищется.Второе совпадение завершается успешно, если входные и / или значения таблицы Match2 равны нулю, или когда они оба имеют одинаковое (не нулевое) значение.

Мой вопрос: существует ли более эффективное (или даже более)читаемый) способ сделать это?

Я использовал этот метод несколько раз, и каждый раз я чувствую себя немного запачканным (субъективная грязь, по общему признанию).

Ответы [ 5 ]

4 голосов
/ 29 сентября 2010

Избегать ИЛИ и НЕ ЗНАТЬ и т. Д.

  • Бит EXCEPT не возвращает строк, если любая из сторон равна NULL
  • Match2 <> @ Match2 означает исключить ненулевое несоответствие

Примерно так

DROP TABLE dbo.Table1

CREATE TABLE dbo.Table1 (ID int NOT NULL, Match1 int NOT NULL, Match2 int NULL)
INSERT dbo.Table1 VALUES (1, 55, 99), (2, 55, NULL)

DECLARE @Match1 int = 55, @Match2 int

SELECT ID
FROM
    (
    SELECT ID FROM Table1 WHERE Match1 = @Match1
    EXCEPT -- @Match2 = NULL, match both rows (99, NULL)
    SELECT ID FROM Table1 WHERE Match2 <> @Match2
    ) foo

SET @Match2 = -1
SELECT ID
FROM
    (
    SELECT ID FROM Table1 WHERE Match1 = @Match1
    EXCEPT -- @Match2 = -1, match ID = 2 only where Match2 IS NULL
    SELECT ID FROM Table1 WHERE Match2 <> @Match2
    ) foo
4 голосов
/ 29 сентября 2010

Есть ли более эффективный (или даже более читабельный) способ сделать это?

Пример, который вы предоставили, используя COALESCE / etc, non-sargable ,Вы должны разделить вещи так, чтобы в запросе запускалось только то, что должно присутствовать :

DECLARE @ID int

IF @Match2 IS NOT NULL
BEGIN

  SELECT @ID = t.id
    FROM TABLE1 t
   WHERE t.match1 = @Match1
     AND (t.match2 = @Match2 OR t.match2 IS NULL)

END
ELSE
BEGIN

  SELECT @ID = t.id
    FROM TABLE1 t
   WHERE t.match1 = @Match1

END

SELECT @ID ID

Если вы хотите, чтобы это происходило в одном операторе SQL, динамический SQL является единственнымреальная альтернатива.Я настоятельно рекомендую прочитать Проклятие и благословение динамического SQL , прежде чем читать дальше:

DECLARE @SQL NVARCHAR(MAX)
    SET @SQL = N' SELECT @ID = t.id
                    FROM TABLE1 t
                   WHERE t.match1 = @Match1 '

    SET @SQL = @SQL + CASE 
                        WHEN @Match2 IS NOT NULL THEN
                          ' AND (t.match2 = @Match2 OR t.match2 IS NULL) ' 
                        ELSE 
                          ' '
                      END

BEGIN

  EXEC sp_executesql @SQL,
                     N'@ID INT OUTPUT, @Match1 VARCHAR(10), @Match2 VARCHAR(10)',
                     @ID, @Match1, @Match2

END
1 голос
/ 29 сентября 2010

Не знаю, предпочтительнее ли это.

SELECT @ID = ID
FROM Table1
WHERE Match1 = @Match1
    AND ((Match2 = @Match2) OR Coalesce(Match2,@Match2) IS NULL)
0 голосов
/ 29 сентября 2010

Я бы подумал, что это должно сработать - при условии, что значение @Match2 будет NULL, если оно необязательно.

CREATE PROC sp_Find_ID (
    @Match1 varchar(10),
    @Match2 varchar(10)
) AS

DECLARE @ID int

SELECT @ID = ID
FROM Table1
WHERE Match1 = @Match1
    AND Match2 = IsNull(@Match2, Match2)

SELECT @ID ID 
0 голосов
/ 29 сентября 2010

мне кажется простым? Должно быть, я что-то упустил .. тебе не нужна коалесция

SELECT @ID = ID
 FROM Table1
 WHERE Match1 = @Match1
    AND (
          (Match2 is null and  @Match2 is null)
           or
           @Match2=Match2
    )

SELECT @ID ID
...