2009-06-18 5 views
3

Я имею запрос SQL (MSSQLSERVER) где добавить столбцы в результирующем с помощью подзапросов:SQL: селективные подзапросов

SELECT P.name, 
(select count(*) from cars C where C.type = 'sports') AS sportscars, 
(select count(*) from cars C where C.type = 'family') AS familycars, 
(select count(*) from cars C where C.type = 'business') AS businesscars 
FROM people P 
WHERE P.id = 1; 

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

В приведенном выше примере каждая запись в таблице «люди» также содержит три дополнительных столбца: «хочетСпортспорт», «хочетФаммобиль» и «хочетБизнескар». Теперь то, что я хочу сделать, это сделать только подзапрос каждого дополнительного столбца, если соответствующее поле «хочет .....» в таблице people установлено в «true». Другими словами, я хочу только сделать первый подзабор, если для параметра P.wantsSportscar установлено значение true для этого конкретного человека. Второй и третий подвыборы должны работать аналогичным образом.

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

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

Если человек А хочет спортивный автомобиль и семейный автомобиль, в результате будут указаны столбцы «имя», «спортивные автомобили» и «семейные автомобили».

Если человек B хочет бизнес-машину, в результате будут указаны столбцы «имя» и «деловой автомобиль».

Я пытался использовать различные комбинации с операторами IF, CASE и EXISTS, но до сих пор я не смог получить синтаксически правильное решение. Кто-нибудь знает, возможно ли это? Обратите внимание, что запрос будет сохранен в хранимой процедуре.

ответ

5

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

Невозможно изменить макет результатов в одном запросе.

Вместо этого, вы можете создать свой запрос следующим образом:

SELECT P.name, 
     CASE WHEN wantssport = 1 THEN (select count(*) from cars C where C.type = 'sports') ELSE NULL END AS sportscars, 
     CASE WHEN wantsfamily = 1 THEN (select count(*) from cars C where C.type = 'family') ELSE NULL END AS familycars, 
     CASE WHEN wantsbusiness = 1 THEN (select count(*) from cars C where C.type = 'business') ELSE NULL END AS businesscars 
FROM people P 
WHERE P.id = 1 

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

Обратите внимание, что реляционная модель отвечает на запросы в терминах relations.

В вашем случае это соотношение выглядит следующим образом: «этот человек нуждается в этом множестве спортивных автомобилей, многих бизнес-автомобилей и многих семейных автомобилей».

Реляционная модель всегда отвечает на этот вопрос с четвертичным отношением.

Он не опускает ни одного из членов отношений: вместо этого он просто устанавливает их в NULL, который является способом SQL, чтобы показать, что член отношения не определен, применим или значим.

+0

Я думаю, это решило мою проблему для меня (см. Мой комментарий в ответ на ответ ChrisCM). Это решение позволит мне проверить значение разных столбцов в моем коде программирования и предпринять соответствующие действия. Это достаточно для меня. Благодарю. – Jerry

0

Я, в основном, парень из Oracle, но есть очень высокая вероятность того же. Если я не понял, то, что вы хотите, невозможно на этом уровне - у вас всегда будет статическое количество столбцов. Ваш запрос может контролировать, если столбец пуст, но поскольку во внешней части запроса вы указали X количество столбцов, вы гарантированно получите столбцы X в своем наборе результатов.

Как я уже сказал, я не знаком с MS SQL Server, но я предполагаю, что какой-то способ выполнения динамического SQL будет выполнен, и в этом случае вам следует исследовать это, поскольку он должен позволить вам построить более гибкий запрос.

+0

Хорошо, статическое количество колонок звучит разумно. Это заставило меня подумать, потому что столбец может существовать, но если в соответствующем столбце «хочет ....» установлено значение «ложь», я могу поместить определенное значение в столбец результатов. Возможно ли оставить этот столбец там, но установить его значение -1 или что-то подобное на основе значения в поле «хочет ....»? – Jerry

0

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

0

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

Вы не сможете сделать это в простом SQL. Я предлагаю вам просто сделать эту колонку NULL или ZERO.

Если вы хотите, чтобы запрос динамически расширялся при добавлении новых автомобилей, PIVOTing может вам помочь.

0

Существует три основных принципа, которые вы хотите научить, чтобы облегчить эту работу. Первая - нормализация данных, вторая - GROUP BY, а третья - PIVOT.

Во-первых, нормализация данных. Ваш дизайн таблицы людей не в первой нормальной форме. Столбцы «wantports», «wantfamily», «wantbusiness» на самом деле являются повторяющейся группой, хотя они могут и не выглядеть так. Если вы можете изменить дизайн таблицы, вам будет выгодно создать третью таблицу, назовите ее «peoplewant», с двумя ключевыми столбцами, персоной и карточкой. Я могу подробно рассказать о том, почему этот дизайн будет более гибким и мощным, если вам нравится, но сейчас я пропущу это.

В ГРУППЕ BY. Это позволяет создать результат, который суммирует каждую группу в одной строке результата.

SELECT 
    p.name, 
    c.type, 
    c.count(*) as carcount 
FROM people p, 
    INNER JOIN peoplewant pw ON p.id = pw.personid 
    INNER JOIN cars c on pw.cartype = c.type 
WHERE 
    p.id = 1 
GROUP BY 
    p.name, 
    c.type 

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

И наконец, PIVOT. Инструмент PIVOT в вашей СУБД позволяет превратить этот результат в форму, где есть только одна строка для человека, и для каждого из файлов, желаемых этим человеком, есть отдельный столбец. Я сам не использовал PIVOT, поэтому я позволю кому-то еще отредактировать этот ответ, чтобы привести пример с помощью PIVOT.

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

0

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

Динамический SQL - ваш ответ.

Прежде чем я скажу, как это сделать, я хочу предисловие к этому, Dynamic SQL - очень опасная вещь, если вы не дезинфицируете свой вход из приложения.

Итак, следовательно, действовать с осторожностью:

declare @sqlToExecute nvarchar(max); 
declare @includeSportsCars bit; 
declare @includeFamilyCars bit; 
declare @includeBusinessCars bit; 

set @includeBusinessCars = 1 
set @includeFamilyCars = 1 
set @includeSportsCars = 1 

set @sqlToExecute = 'SELECT P.name ' 

if @includeSportsCars = 1 
    set @sqlToExecute = @sqlToExecute + '(select count(*) from cars C where C.type = ''sports'') AS sportscars, '; 
if @includeFamilyCars = 1 
    set @sqlToExecute = @sqlToExecute + '(select count(*) from cars C where C.type = ''family'') AS familycars, '; 
if @includeBusinessCars = 1 
    set @sqlToExecute = @sqlToExecute + '(select count(*) from cars C where C.type = ''business'') AS businesscars ' 

set @sqlToExecute = @sqlToExecute + ' FROM people P WHERE P.id = 1;'; 

exec(@sqlToExecute) 
Смежные вопросы