我正在使用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;

上面的查询只是来自一个有点无稽之谈的测试设置,但它充分考虑了我的想法。我实际上正在处理的查询涉及许多复杂的表格,这些表格只会分散手头的问题。

在上面的例子中,表格中的每个记录都是“人”。还有三个附加列:“wantsSportscar”,“wantsFamilycar”和“wantsFamscar”。和“wantsBusinesscar”。现在我想要做的只是在相应的“想要......”时进行每个附加列的子选择。人员表中的字段设置为“true”。换句话说,如果P.wantsSportscar对于特定的人设置为true,我只想做第一个子选择。第二个和第三个子选择应该以类似的方式工作。

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

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

如果A人想要跑车和家用车,结果将包括列“名称”,“sportscars”等。和“家庭电器”。

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

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

有帮助吗?

解决方案

在您的情况下,可以使用 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 的方式来显示一个成员关系没有定义,适用或有意义。

其他提示

我主要是甲骨文家伙,但同样适用的机会很高。除非我误解了,你想要的东西在那个级别是不可能的 - 你总是会有一个静态的列数。您的查询可以控制列是否为空,但由于在查询的最外部分中您指定了X列,因此您可以保证在结果集中获得X列。

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

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

  

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

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

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

您希望学习三种基础知识,以使这项工作变得轻松。第一个是数据规范化,第二个是GROUP BY,第三个是PIVOT。

首先,数据规范化。您的人员表的设计不是第一次正常形式。列“wantports”,“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

此(未经测试的)查询会为您提供所需的结果,但结果对于每个人想要的每种车型都有一个单独的行。

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

如果您使用相同的技术在一次扫描中检索多个人的数据,请记住,对于任何人想要的每个所需类型都会显示一列,并且对于不想要的人,PIVOT结果中会显示零。结果列中的汽车类型。

刚刚通过谷歌搜索发现了这篇文章,所以我意识到我迟到了这个派对,但是......确定这真的是可能要做...但是,我不建议实际上这样做,因为它通常被认为是非常糟糕的事情(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)
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top