4

我有一个 SQL 查询(MSSQLSERVER),我在其中使用子选择将列添加到结果集中:

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”表中的每条记录还具有三个附加列:“wantsSportscar”、“wantsFamilycar”和“wantsBusinesscar”。现在我想做的只是如果人员表中的相应“想要.....”字段设置为“真”,则只对每个附加列进行子选择。换句话说,如果 P.wantsSportscar 为那个特定的人设置为 true,我只想做第一个子选择。第二个和第三个子选择应该以类似的方式工作。

因此,此查询的工作方式应该是显示特定人员的姓名以及他想要拥有的汽车类型的可用车型数量。可能值得注意的是,我的最终结果集将始终只包含一条记录,即一个特定用户的记录。

重要的是,如果一个人对某种类型的汽车不感兴趣,那么该类型的列将不会包含在最终结果集中。一个确保这一点很清楚的例子:

如果 A 想要一辆跑车和一辆家庭车,则结果将包括“名称”、“跑车”和“家庭车”列。

如果 B 想要商务车,则结果将包括“名称”和“商务车”列。

我一直在尝试将各种组合与 IF、CASE 和 EXISTS 语句结合使用,但到目前为止,我还无法获得语法正确的解决方案。有谁知道这是否可能?请注意,查询将存储在存储过程中。

4

6 回答 6

6

在您的情况下,可能存在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

在你的情况下,关系如下:“这个人的需求满足了这么多跑车,这么多商务车和这么多家用车”。

关系模型总是用四元关系回答这个特定的问题。

它没有省略任何关系成员:相反,它只是将它们设置为NULLwhich is the SQL' 表示关系成员未定义、适用或有意义的方式。

于 2009-06-18T12:04:46.260 回答
0

我主要是甲骨文的人,但很有可能同样适用。除非我误解了,否则您想要的在那个级别是不可能的 - 您将始终拥有静态数量的列。您的查询可以控制该列是否为空,但由于在查询的最外层部分您指定了 X 列,因此您可以保证在结果集中获得 X 列。

正如我所说,我不熟悉 MS SQL Server,但我猜会有一些执行动态 SQL 的方法,在这种情况下你应该研究它,因为它应该允许你构建一个更灵活的查询。

于 2009-06-18T12:02:43.690 回答
0

您可以通过首先将值作为单独的行选择到临时表中,然后在该表上执行 PIVOT(将行转换为列)来执行您想要的操作。

于 2009-06-18T12:08:44.177 回答
0

重要的是,如果一个人对某种类型的汽车不感兴趣,那么该类型的列将不会包含在最终结果集中。一个确保这一点很清楚的例子:

您将无法在纯 SQL 中执行此操作。我建议您只将此列设为 NULL 或 ZERO。

如果您希望在添加新车时动态扩展查询,那么 PIVOTing 可以为您提供一些帮助。

于 2009-06-18T12:11:10.740 回答
0

为了使这项工作变得简单,您需要学习三个基础知识。第一个是数据规范化,第二个是 GROUP BY,第三个是 PIVOT。

首先,数据规范化。您对 people 表的设计不是第一范式。“wantsports”、“wantfamily”、“wantbusiness” 列确实是一个重复的组,尽管它们可能看起来不像一个。如果您可以修改表设计,您会发现创建第三个表是有利的,我们称之为“peoplewant”,具有两个关键列,personid 和 cartype。如果您愿意,我可以详细说明为什么这种设计会更加灵活和强大,但我现在将跳过它。

到 GROUP 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

这个(未经测试的)查询为您提供了您想要的结果,除了结果对于人们想要的每种汽车类型都有一个单独的行。

最后,枢轴。DBMS 中的 PIVOT 工具允许您将此结果转换为一个表格,其中该人只有一行,并且该人想要的每种车型都有一个单独的列。我自己没有使用 PIVOT,所以我会让其他人编辑此响应以提供使用 PIVOT 的示例。

如果您使用相同的技术在一次扫描中检索多个人的数据,请记住,对于任何人想要的每种想要的类型都会出现一列,而对于不想要汽车类型的人,PIVOT 结果中将出现零那是在结果列中。

于 2009-06-18T12:33:59.243 回答
0

刚刚通过谷歌搜索看到这篇文章,所以我意识到我参加这个聚会有点晚了,但是..当然这确实是可能的......但是,我不建议真的这样做,因为它通常被认为是非常糟糕的事情(tm)。

动态 SQL 就是您的答案。

在我说怎么做之前,我想先说一下,如果你不清理来自应用程序的输入,动态 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)
于 2009-10-21T23:37:14.250 回答