具有复杂where子句

时间:2015-05-28 13:24:38

标签: sql sql-server

我有一张表格,其中存储了有关汽车更改的详细信息(零件更改)。

例如,这里是CarChangeHistory表,对几辆车中的零件进行了少量更改(零件名称在另一个表中预定义,并且永远不会与该默认列表不同):

CarChangeHistory
CarGUID PartName    PNPNNewValue TimeOfChange
X123    Windshield  344DFSS
X133    Lights      BN23112
X899    Battery     DNNN222
X433    Battery     SM12345

汽车表看起来像这样:

CarTable
CarGUID Lights Windshield Battery Handle Door Mfg
X123    11111  344DFSS

CarChangeHistory表中还有许多其他类似的条目。

如果以下where子句为真,我想执行搜索并返回属于最近7天窗口内的所有项目:

(Lights LIKE %BN%) AND (Battery = 'DLK222' OR Windshield = true) AND (...) AND (...)

我可以将其转换为与我的表匹配到类似的东西(这是更合乎逻辑的伪代码。因为如果我将在SQL查询中单独使用它将不返回任何内容。因为上面的AND连接将尝试对每个原始进行AND,但我想在过去7天内每N次更改/原始数量执行此操作):

(PartName = 'Lights' AND PNNewValue LIKE  '%BN%') 
AND  
((PartName = 'Battery' AND PNNewValue =  'DLK222')  OR  (PartName = 'Windshield')) 
AND
(...)
AND
(...)

所以..如果我忽略空(...),使用上面的示例表,它会返回:

X123    Windshield  344DFSS
X133    Lights      BN23112

如果我的示例没有与Lights对齐,则不返回任何内容......

我猜最大的问题是AND连接,我如何在这样的查询中对待它?如何使用此where子句执行此类搜索?

我已经尝试了以下内容并且它有效,但我需要更改where子句(通过将(Lights LIKE %BN%) AND (Battery = 'DLK222' OR Windshield = true)扩展为(Lights LIKE %BN% OR Windshield = true) OR (Lights LIKE %BN% OR Battery = 'DLK222'))。我想如果我有更多的条件,它会变得相当复杂。

SELECT TimeOfChange, CarGUID 
FROM CarChangeHistory
WHERE (
(((PartName = 'Lights' AND PNNewValue LIKE '%BN%') OR (PartName = 'Battery' AND PNNewValue = 'DLK222')))
OR
(((PartName = 'Lights' AND PNNewValue LIKE '%BN%') OR (PartName = 'Windshield')))
) AND TimeOfChange BETWEEN DATEADD(DAY,-7,GETDATE()) AND GETDATE()
GROUP BY TimeOfChange, CarGUID
HAVING COUNT(*) = 2

有谁知道这个问题的更好解决方案?转换我的逻辑where子句的最佳方法是什么,如果我运行的话,它只会返回任何内容,它可以在过去7天(或任何时间窗口)中实际过滤表中的数据。

1 个答案:

答案 0 :(得分:1)

如果我理解正确,您希望在历史记录表中查找更改。此表没有名为Windshield,Lights等的列,而是包含PartName列。因此,对于一条记录,您有一个PartName。它不能同时是'挡风玻璃'和'灯'。这为您提供了两个选项:

1)使用EXISTS条款:

select carguid
from cars c
where exists
(
  select * 
  from carchangehistory cch
  where cch.carguid = c.carguid
  and partname = 'Lights' and pnnewvalue like  '%BN%'
  and timeofchange between dateadd(day,-7,getdate()) and getdate()
)
and exists
(
  select * 
  from carchangehistory cch
  where cch.carguid = c.carguid
  and ((partname = 'Battery' and pnnewvalue =  'DLK222') or (partname = 'Windshield')) 
  and timeofchange between dateadd(day,-7,getdate()) and getdate()
)
and exists
(
  ...
)

这将为您提供所有在过去7天内进行了所有查找更改的汽车。但是它一次又一次地查询同一个表。

2)使用带有条件HAVING子句的GROUP BY

  select carguid
  from carchangehistory
  where timeofchange between dateadd(day,-7,getdate()) and getdate()
  group by carguid
  having count(case when partname = 'Lights' and pnnewvalue like '%BN%' then 1 end) > 0
  and count(case when (partname = 'Battery' and pnnewvalue =  'DLK222') or partname = 'Windshield' then 1 end) > 0
  and count(case when ... then 1 end) > 0
  ...;

这会给你相同的结果,但只扫描你的历史记录表一次。