在where子句中使用复合选择项

时间:2009-08-12 14:59:58

标签: tsql syntax

这个问题最好用一个简单的例子来表达。

为什么我不能这样做?

select (lastname + ', ' + firstname) as fullname
from people
where fullname = 'Bloggs, Joe'

相反,我必须这样做:

select (lastname + ', ' + firstname) as fullname
from people
where (lastname + ', ' + firstname) = 'Bloggs, Joe'

对我来说闻起来很糟糕。

查询越复杂,问题就越严重。

后续

以下是基于问题产生的现实问题的更好示例。

SELECT ClientID, 
       Name, 
       ContractStartDate, 
       ContractDetails.ContractLength, 
       DATEADD(MONTH, ContractDetails.ContractLength, ContractStartDate) 
           as ContractEndDate
FROM Clients
LEFT OUTER JOIN ContractDetails 
    ON Clients.ClientID = ContractDetails.ClientID
WHERE DATEADD(MONTH, ContractDetails.ContractLength, ContractStartDate) 
      > '2009-06-30'

我已根据建议重写了查询以使用嵌入式视图。但是它仍然包含重复 - 但这次是加入。

SELECT ClientID, 
       Name, 
       contractStartDate, 
       ContractDetails.ContractLength, 
       contractEndDate
FROM (
      SELECT ClientID, 
             Name, 
             ContractStartDate, 
             DATEADD(MONTH, ContractDetails.ContractLength, contractStartdate) 
               AS contractEndDate
      FROM Clients
      LEFT OUTER JOIN ContractDetails 
        on Clients.ClientID = ContractDetails.ClientID
      ) myview
LEFT OUTER JOIN ContractDetails 
  on myview.ClientID = ContractDetails.ClientID
WHERE myview.ContractEndDate > '2009-06-30'
ORDER BY ClientID

此查询的目的是在特定时间点查找所有实时客户端,其中不保留历史状态数据(即从已知合同开始日期和长度计算合同结束日期)。

有人能想出一种消除这种重复的方法吗?

最终跟进

罗宾日帮助我解决了我在这里失踪的关键问题,实际上让我删除了重复。然而,KM有一个观点,他说WHERE应该在嵌套视图上,而不是最终结果,这将需要复制部分语句(这是我试图避免的)。 在这种特殊情况下我可以逃脱它,因为我知道在ContractDetails表中没有数百万条记录,而且永远不会。

SELECT ClientID, 
   Name, 
   ContractStartDate, 
   myview.ContractLength, 
   ContractEndDate
FROM (
  SELECT ClientID, 
         Name, 
         ContractStartDate, 
         DATEADD(MONTH, ContractDetails.ContractLength, ContractStartdate) 
           AS ContractEndDate,
         ContractDetails.ContractLength as Length
  FROM Clients
  LEFT OUTER JOIN ContractDetails
    on Clients.ClientID = ContractDetails.ClientID
  ) myview
WHERE myview.ContractEndDate > '2009-06-30'
ORDER BY ClientID

5 个答案:

答案 0 :(得分:3)

您可以使用派生表/嵌套视图...

select
    fullname
from
(
    select
        (lastname + ', ' + firstname) as fullname
    from
        people
) myview
where
    myview.fullname = 'Bloggs, Joe'

编辑:只是为了澄清,这是为了展示你所询问的概念。在此特定示例中,WHERE子句应检查firstname ='Joe'和lastname ='Bloggs',因为KM已经回答,而不是检查全名。

答案 1 :(得分:1)

尝试:

select
    (lastname + ', ' + firstname) as fullname
    from people
    where lastname = 'Bloggs' AND firstname='Joe'

不要根据格式化的输出“fullname”进行过滤,根据列进行过滤,这些列应该在索引中。

修改
根据您修改后的问题:

将条件放在派生表中以限制它(并使其尽可能小)。通过单独执行此操作,我看到查询速度更快。我确信查询引擎足够聪明,不能两次执行DATEADD(),所以不用担心。

SELECT ClientID, 
       Name, 
       contractStartDate, 
       ContractDetails.ContractLength, 
       contractEndDate
FROM (
      SELECT ClientID, 
             Name, 
             ContractStartDate, 
             DATEADD(MONTH, ContractDetails.ContractLength, contractStartdate) 
               AS contractEndDate
      FROM Clients
      LEFT OUTER JOIN ContractDetails 
        on Clients.ClientID = ContractDetails.ClientID
      WHERE DATEADD(MONTH, ContractDetails.ContractLength, contractStartdate) > '2009-06-30'
      ) myview
LEFT OUTER JOIN ContractDetails 
  on myview.ClientID = ContractDetails.ClientID
ORDER BY ClientID

答案 2 :(得分:1)

选择列表返回的虚拟表的转换,其中订单条款。这些子句不知道选择列表。此外,子句中定义的列的任何转换都不是可搜索的,强制SQL执行表或索引扫描。换句话说,这样做绝对会扼杀性能。

答案 3 :(得分:1)

您编辑的示例是否过于复杂?出了什么问题:

SELECT *
FROM (
      SELECT ClientID, 
             Name, 
             ContractStartDate, 
             ContractLength,
             DATEADD(MONTH, ContractDetails.ContractLength, contractStartdate) 
               AS contractEndDate
      FROM Clients
      LEFT OUTER JOIN ContractDetails 
        on Clients.ClientID = ContractDetails.ClientID
      ) myview
WHERE myview.ContractEndDate > '2009-06-30'
ORDER BY ClientID

答案 4 :(得分:0)

如何使此查询与IN和多列一起使用?这适用于Oracle,但不适用于T-SQL。

select (lastname + ', ' + firstname) as fullname
from people
where ((lastname, firstname)) IN (('Bloggs', 'Joe'))