SQL, который может вытягивать записи на три возможных значения или ни на одно - PullRequest
0 голосов
/ 18 сентября 2009

Скажем, у вас есть WIDGETS таблица:

  • WidgetID int
  • ColorID int (значения поиска: красный, синий, зеленый, желтый, черный, коричневый)
  • SizeID int (значения поиска: маленький, средний, большой, большой)
  • Weight int (Значения поиска: UltraLight, Light, Normal, Heavy, UltraHeavy

Хорошо, это общая идея стола. Мне не нужны имена поиска, у меня есть идентификаторы поиска. Итак, из этой таблицы я бы смог получить списки, которые встречаются, скажем, по следующим критериям:

  1. Показать мне все виджеты красного, синего или черного цвета
  2. Показать мне все виджеты красного или синего и малого или среднего размера
  3. Показать мне все виджеты, которые только Heavy
  4. Покажите мне все виджеты, которые тяжелые и желтые
  5. Покажите мне все виджеты, которые тяжелые и желтые и маленькие или большие
  6. Показать мне все виджеты, которые только Large
  7. Показать мне все виджеты, которые только зеленые

Получите идею? Я пытался работать с Stored Proc, который позволил мне отправить некоторые параметры. Даже пробовал динамический sql, но получал странные ошибки. не могу вспомнить сейчас.

Моя попытка № 3, вроде бы работает, если бы я мог заставить ее работать в Proc и быть в состоянии определить, какое соединение я хочу

Примеры того, что я пробовал:

Примечание: В моих примерах: (CourseID - это WidgetID) и (StateID - это ColorID) и (CreditTypeID - SizeID) и (SubjectID - WeightID)

Метод попытки 1

ALTER PROCEDURE [dbo].[CourseListFullInfoByStateCreditSubject]
  @StateIDs VARCHAR(200) = '',
  @CreditTypeIDs VARCHAR(200) = '',
  @SubjectTypeIDs VARCHAR(200) = ''
AS

BEGIN

  DECLARE @SQL AS NVARCHAR(MAX)
  SET @SQL = 'SELECT DISTINCT
                     C.CourseID,
                     LU.FirstName,
                     LU.LastName,
                     (SELECT COUNT(ReviewID) FROM Review AS R WHERE R.CourseID = C.CourseID) AS ReviewCount
                FROM [Course] AS C WITH(NOLOCK)
                JOIN LexUser AS LU ON LU.LexUserID = C.PresenterID '

If @StateIDs IS NOT NULL AND @StateIDs <> '''' AND @StateIDs <> '0'
BEGIN
  SET @SQL = @SQL + ' JOIN CourseToState AS CS ON CS.CourseID = C.CourseID AND CHARINDEX('','' + CAST(CS.StateID AS VARCHAR) + '','', '','' + @StateIDs + '','') > 0 '
END

If @CreditTypeIDs IS NOT NULL AND @CreditTypeIDs <> '' AND @CreditTypeIDs <> '0'
BEGIN
  SET @SQL = @SQL + 'JOIN CourseToCreditType As CC ON CC.CourseID = C.CourseID AND CHARINDEX('','' + CAST(CC.CreditTypeID AS VARCHAR) + '','', '','' + @CreditTypeIDs + '','') > 0 '
END

If @SubjectTypeIDs IS NOT NULL AND @SubjectTypeIDs <> '' AND @SubjectTypeIDs <> '0'
BEGIN
  SET @SQL = @SQL + 'JOIN CourseToSubject As CSu ON CSu.CourseID = C.CourseID AND CHARINDEX('','' + CAST(CSu.SubjectID AS VARCHAR) + '','', '','' + @SubjectTypeIDs + '','') > 0 '
END

EXEC sp_executesql @SQL

При попытке 1 я пытаюсь отправить в ID:

[CourseListFullInfoByStateCreditSubject] ''1,2,4'', ''0'', ''0''

... но я получаю сообщение об ошибке "Неверный синтаксис рядом с '1'."

Попытка метода 2, выдает ту же ошибку

DECLARE @SQL AS NVARCHAR(MAX)
SET @SQL = 'SELECT DISTINCT
                   C.CourseID,
                   LU.FirstName,
                   LU.LastName,
                   (SELECT COUNT(ReviewID) 
                     FROM Review AS R 
                    WHERE R.CourseID = C.CourseID) AS ReviewCount
             FROM [Course] AS C WITH(NOLOCK)
             JOIN LexUser AS LU ON LU.LexUserID = C.PresenterID '

If @StateIDs IS NOT NULL AND @StateIDs <> '''' AND @StateIDs <> '0'
BEGIN
  SET @SQL = @SQL + ' JOIN CourseToState AS CS ON CS.CourseID = C.CourseID AND CHARINDEX('','' + CAST(CS.StateID AS VARCHAR) + '','', '','' + @StateIDs + '','') > 0 '
END

If @CreditTypeIDs IS NOT NULL AND @CreditTypeIDs <> '' AND @CreditTypeIDs <> '0'
BEGIN
  SET @SQL = @SQL + 'JOIN CourseToCreditType As CC ON CC.CourseID = C.CourseID AND CHARINDEX('','' + CAST(CC.CreditTypeID AS VARCHAR) + '','', '','' + @CreditTypeIDs + '','') > 0'
END

If @SubjectTypeIDs IS NOT NULL AND @SubjectTypeIDs <> '' AND @SubjectTypeIDs <> '0'
BEGIN
  SET @SQL = @SQL + 'JOIN CourseToSubject As CSu ON CSu.CourseID = C.CourseID AND CHARINDEX('','' + CAST(CSu.SubjectID AS VARCHAR) + '','', '','' + @SubjectTypeIDs + '','') > 0'
END

Метод попытки 3 - Вид работ Этот метод работает, только если я отправляю ему идентификаторы для каждого поля, я не могу оставить один пробел, чтобы получить «все», и не могу заставить его работать в хранимом процессе, что я и пытался сделать в «Попытке № 2»

DECLARE @StateIDs VARCHAR(200) = ''
DECLARE @CreditTypeIDs VARCHAR(200) = ''
DECLARE @SubjectTypeIDs VARCHAR(200) = ''
SET @StateIDs = '1,3,2,'
SET @CreditTypeIDs = '1,3'
SET @SubjectTypeIDs = '1,2,3,4' 

SELECT DISTINCT
       C.CourseID,
       LU.FirstName,
       LU.LastName,
       (SELECT COUNT(ReviewID) FROM Review AS R WHERE R.CourseID = C.CourseID) AS ReviewCount
  FROM [Course] AS C WITH(NOLOCK)
  JOIN LexUser AS LU ON LU.LexUserID = C.PresenterID 
  JOIN CourseToState AS CS ON CS.CourseID = C.CourseID AND CHARINDEX(',' + CAST(CS.StateID AS VARCHAR) + ',', ',' + @StateIDs + ',') > 0 
  JOIN CourseToCreditType As CC ON CC.CourseID = C.CourseID AND CHARINDEX(',' + CAST(CC.CreditTypeID AS VARCHAR) + ',', ',' + @CreditTypeIDs + ',') > 0
  JOIN CourseToSubject As CSu ON CSu.CourseID = C.CourseID AND CHARINDEX(',' + CAST(CSu.SubjectID AS VARCHAR) + ',', ',' + @SubjectTypeIDs + ',') > 0

Ответы [ 4 ]

0 голосов
/ 29 октября 2009

Обязательным условием для этого решения является разделенная табличная функция. В сети много примеров, но я рекомендую использовать вариант, который использует таблицу Tally или «Numbers», так как она самая быстрая. Как только вы это сделаете, решение будет несколько тривиальным:

Declare @StateIdList nvarchar(max)
Declare @CreditTypeIdList nvarchar(max)
Declare @SubjectTypeIds nvarchar(max)

Select F1....Fn
From Course As C
Where   (
        @StateIdList Is Null 
        Or  Exists(
                    Select 1
                    From CourseToState As CS1
                        Join dbo.Split(@StateIdList) As S1
                            On S1.Id = CS1.Id
                    Where CS1.CourseId = C.CourseId
                    )
        )
    And (
        @CreditTypeIdList Is Null 
        Or  Exists(
                    Select 1
                    From CourseToCreditType As CT1
                        Join dbo.Split(@CreditTypeIdList) As S1
                            On S1.Id = CT1.Id
                    Where CT1.CourseId = C.CourseId
                    )
        )
    And (
        @SubjectTypeIds Is Null 
        Or  Exists(
                    Select 1
                    From CourseToSubject  As CSu1
                        Join dbo.Split(@CreditTypeIdList) As S1
                            On S1.Id = CSu1.Id
                    Where CSu1.CourseId = C.CourseId
                    )
        )   
0 голосов
/ 18 сентября 2009

Как насчет этого:

Создайте хранимую процедуру с разделенным запятыми списком условий, должен выглядеть как

DECLARE @colorConditions VARCHAR(100)
SET @colorConditions = ' Red, Blue, Green, Yellow, Black, Brown, '

DECLARE @sizeConditions VARCHAR(100)
SET @sizeConditions = ' Small, Med, Big, Large, '

DECLARE @weightConditions VARCHAR(100)
SET @weightConditions = ' UltraLight, Light, Normal, Heavy, UltraHeavy, '

DECLARE @SQL VARCHAR(1000)
SET @SQL = 
     'SELECT * 
      FROM Widgets W
      INNER JOIN Colors C 
        ON W.ColorID = C.ColorID AND ''' + @colorConditions + ''' LIKE ''% '' + C.Color + '', %'' 
      INNER JOIN Sizes S 
        ON W.SizeID = S.SizeID AND ''' + @sizeConditions + ''' LIKE ''% '' + S.Size + '', %''
      INNER JOIN Weight WT 
        ON W.WeightID = W.WeightID AND ''' + @weightConditions + ''' LIKE '' %'' + WT.Weight + '', %'''

EXEC (@SQL)

Ничего особенного здесь. Все, что он делает, это сопоставляет каждую черту (Цвет, Размер, Вес) с разделенным запятыми списком квалификационных условий. Если вы хотите фильтровать синие элементы, вы должны передать строку «Синий». Боковые пробелы и запятые должны присутствовать, потому что их легче иметь, чем бороться с SQL. Затем строка условия сравнивается с каждым значением цвета следующим образом:

Condition string: 

' Blue, Green, '

Colors: 

Red
Blue
Green

...AND ' Blue, Green, ' LIKE '% Red, %' -- fails
...AND ' Blue, Green, ' LIKE '% Blue, %' -- succeeds
...AND ' Blue, Green, ' LIKE '% Green, %' -- succeeds

Это должно выполняться динамически, если только вы не разделите условия как-то на временную таблицу и не используете оператор IN.

0 голосов
/ 18 сентября 2009

Попробуйте это ... вызвать его, для каждого параметра передать значение null, чтобы игнорировать, или битовую маску комбинация значений, которые вы хотите включить. согласно следующей таблице ...

-- ----Color ----
Red    =  1
Blue   =  2
Green  =  4
Yellow =  8
Black  = 16
Brown  = 32
Red Or Green = 5

-- ---- Size ---
Small  = 1
Medium = 2
Big    = 4
Large  = 8

-- ----Weight ----
UltraLight =  1
Light      =  2
Normal     =  4
Heavy      =  8
UltraHeavy = 16

Кроме того, измените целочисленные значения поиска в базе данных (как PK в таблицах поиска, так и FK в таблице Widgets), чтобы они имели все соответствующие степени двух (1,2,4,, 8,16). ..)

-- -------------------------
Create procedure GetWidgets
@Color TinyInt = Null, 
@Size TinyInt = Null
@Weight TinyInt = Null
As
Set NoCount On

-- -------------------------------

   Select * From Widgets
   Where Color & IsNull(@Color, Color)> 0
      And Size & IsNull (@Size,Size) > 0 
      And Weight & IsNull (@Weight, Weight ) > 0 

   Return 0
-- -------------------------

0 голосов
/ 18 сентября 2009

Попробуйте левое соединение из виджетов для всех таблиц поиска и отфильтруйте соответствующие строки, используя Where.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...