Хранимая процедура WHERE LIKE ID или ИМЯ - PullRequest
3 голосов
/ 15 декабря 2009

Группа, Я пытаюсь создать хранимую процедуру, используя одну переменную @Customer. То, что я хочу сделать, это вставить что-то в мое предложение WHERE, в котором говорится, что если это число, выполните поиск в поле CustomerID, где введено число LIKE CustomerID ... Если введен символ, найдите поле CustomerName, в котором введен текст LIKE CustomerName. Ниже приведен пример того, что я пытаюсь сделать:

CREATE PROCEDURE [dbo].[uspGetCustomer] (@Customer VARCHAR(100))

AS

SET NOCOUNT ON

SELECT * 
FROM dbo.Customers
WHERE CASE WHEN ISNUMERIC(@Customer) THEN CustomerID LIKE @Customer + '%'
ELSE CustomerName LIKE @Customer + '%' END

Есть предложения?

Ответы [ 10 ]

4 голосов
/ 15 декабря 2009

Я бы сделал это с помощью оператора IF, поскольку включение этой логики в WHERE затрудняет чтение в дальнейшем:

DECLARE @match = @CustomerID + '%'

IF ISNUMERIC(@CustomerID) = 1 
BEGIN
     SELECT * FROM CUSTOMERS WHERE CustomerID LIKE @match
END ELSE BEGIN
     SELECT * FROM CUSTOMERS WHERE CustomerNAME LIKE @match
END  




обновление: Мне интересно, если поле CustomerID является INT. Если так, то я бы изменил запрос следующим образом (и избавился бы от переменной @match):

    ... WHERE CustomerID = Cast(@CustomerID as INT) --if numeric
    ... WHERE CustomerNAME = @CustomerID + '%'      --if not numeric

Однако, если это какое-то странное поле VARCHAR, начинающееся с числа и заканчивающееся другими данными, такими как '11_blah', то подстановочный знак LIKE plus работает отлично

3 голосов
/ 15 декабря 2009

Это может быть только я, но использование одной переменной для представления двух разных полей дает мне ложные советы. Я бы переписал эту хранимую процедуру так, чтобы она принимала две разные переменные, которые можно обнулять (одну int, CustomerID и одну varchar, CustomerName). Это будет выглядеть так:

CREATE PROCEDURE [dbo].[uspGetCustomer] (
@CustomerID int = null, 
@CustomerName VARCHAR(100) = null)

AS

IF @CustomerID IS NOT NULL BEGIN
     SELECT * FROM Customers WHERE CustomerID = @CustomerID
END ELSE IF @CustomerName IS NOT NULL BEGIN
     SELECT * FROM Customers WHERE CustomerName LIKE @CustomerName
END ELSE
     --error handling, return empty set maybe?
END

Если это просто не вариант, вы все равно можете использовать:

CREATE PROCEDURE [dbo].[uspGetCustomer] (@Customer VARCHAR(100))

AS

DECLARE @NameMatch;

IF ISNUMERIC(@Customer) = 1 BEGIN
     SELECT * FROM Customers WHERE CustomerID = CAST (@Customer AS int)
END ELSE BEGIN
     SET @NameMatch = '%' + @Customer + '%'
     SELECT * FROM Customers WHERE CustomerName LIKE @NameMatch
END
3 голосов
/ 15 декабря 2009

Выполнение одного оператора SQL, который пытается решить оба условия, приведет к худшему плану выполнения. Помните, что SQL должен сгенерировать один единственный план для удовлетворения любого значения @variable. В вашем случае, когда @customerID является числовым, правильным планом будет использование индекса для CustomerID. Но когда @customerID является именем, правильным доступом будет индекс для CustomerName. Учитывая эту дилемму, оптимизатор, скорее всего, выберет план, который выполняет полное сканирование, т.е. не оптимизирован ни в одном случае.

Надлежащим образом определить в вашем приложении , если это идентификатор или имя, и вызвать две отдельные хранимые процедуры uspGetCustomerByID и uspGetCustomerByName в соответствии с введенным значением. Если вы должны сделать это через одну «волшебную» точку входа API (всесильную uspGetCustomer), то вы уже получили несколько хороших ответов.

2 голосов
/ 15 декабря 2009

Сделайте что-то вроде этого:

CREATE PROCEDURE [dbo].[uspGetCustomer] (
    @CustomerID INT = NULL
    @Customer VARCHAR(100) = NULL
)

AS

SET NOCOUNT ON

IF @CustomerID is not null
BEGIN
    SELECT * FROM dbo.Customers
    WHERE CustomerID = @CustomerID
END
ELSE
BEGIN
    SELECT * FROM dbo.Customers
    WHERE CustomerName LIKE @CustomerID + '%'
END

SET NOCOUNT OFF
2 голосов
/ 15 декабря 2009

Использование:

CREATE PROCEDURE [dbo].[uspGetCustomer] (@Customer VARCHAR(100))
AS

BEGIN

IF ISNUMERIC(@Customer) = 1
  BEGIN
    SELECT *
      FROM dbo.CUSTOMERS
     WHERE customerid LIKE @Customer + '%'
  END
ELSE
  BEGIN
    SELECT *
      FROM dbo.CUSTOMERS
     WHERE customername LIKE @Customer + '%'
  END

END
1 голос
/ 15 декабря 2009
SELECT  * 
FROM    dbo.Customers
WHERE   ISNUMERIC(@Customer) = 1
        AND CustomerID = CAST(@Customer AS INTEGER)
UNION ALL
SELECT  *
FROM    dbo.Customers
WHERE   NOT ISNUMERIC(@Customer) = 1
        AND CustomerName LIKE @Customer + '%'

При этом будут использоваться соответствующие индексы CustomerID и CustomerName

.
1 голос
/ 15 декабря 2009

Я бы сказал проще и предположил, что имена клиентов никогда не бывают числовыми:

SELECT * 
FROM dbo.Customers
WHERE CustomerID LIKE @Customer + '%'
OR CustomerName LIKE @Customer + '%'

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

WHERE (IsNumeric(@Customer) = 1 AND CustomerID LIKE @Customer + '%')
OR (IsNumeric(@Customer) = 0 AND CustomerName LIKE @Customer + '%')

Но тогда как бы вы искали клиента с числовым именем? И, кстати ... такой поиск найдет клиента 121, если вы ищете 12.

0 голосов
/ 15 декабря 2009

Проще говоря, у каждого есть индекс, определите во внешнем интерфейсе, какой он есть, и установите правильный параметр.

CREATE PROCEDURE dbo.getCustomer 
(@CustomerID int = null, 
@CustomerName VARCHAR(100) = null)
AS
SET NOCOUNT ON

SELECT * 
FROM Customers 
WHERE CustomerID = @CustomerID
or CustomerName like @CustomerName + '%'
0 голосов
/ 15 декабря 2009

О, просто сделай это так ...

IF ISNUMERIC(@Customer) THEN
SELECT * FROM .... CustomerID = @Customer

ELSE 
SELECT * FROM ... CustomerName LIKE @Customer 

Но вы бы хотели, чтобы это было более ремонтопригодным, я полагаю ...

declare @basequery NVARCHAR(4000)
declare @params NVARCHAR(4000)

set @base_query = 'select * from dbo.Customers where '

IF ISNUMERIC(@Customer) THEN
  SET @base_query = @base_query + 'customerid = @xCustomer'
  SET @params = '@xCustomer int'
END
ELSE
  SET @base_query = @base_query + 'customerName LIKE @xCustomer + ''%'' '
  SET @params = '@xCustomer nvarchar(1000)'
END

exec sp_execuresql @base_query, @params, @Customer

Конечно, я бы рекомендовал этот тип динамического sql только для более сложных видов фильтрации.

0 голосов
/ 15 декабря 2009

Я верю, что это сделает это:

WHERE CustomerID LIKE
  CASE WHEN IsNumeric(@Customer) = 1 THEN 
    CustomerID 
  ELSE
    CustomerName + '%'
  END
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...