【问题标题】:SQL Query on single column with a complex where clause具有复杂 where 子句的单列 SQL 查询
【发布时间】:2015-05-28 13:24:38
【问题描述】:

我有一张表,用于存储有关汽车更改(零件更改)的详细信息。

例如,这里是 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 连接会尝试对每个 raw 做 AND,但我想在过去 7 天内每 N 次更改/raw 做一次):

(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 天(或任何时间窗口)内实际过滤表中数据的东西的最佳方法是什么。

【问题讨论】:

  • 这听起来很有趣,但我真的不确定你的问题是什么。您是在问如何将您对集合(这个 AND 那个 AND 这个其他东西)的定义转换为 WHERE 子句?
  • 显示数据示例(您已经完成)和您想要的查询结果示例也会有所帮助。
  • 我不明白你想要达到什么目的。你有很多ands和ors。您能用文字描述您要选择的内容吗?
  • 我正在尝试执行搜索。根据 where 语句(使用 AND 和 OR)查找过去 7 天内 where 发生变化的所有部分。
  • @22332112,对您的问题的最佳描述是一些测试数据和所需的输出。

标签: sql sql-server


【解决方案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) 将 GROUP BY 与条件 HAVING 子句一起使用

  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
  ...;

这会为您提供相同的结果,但只会扫描您的历史记录表一次。

【讨论】:

  • 谢谢!第二种解决方案效果很好。而且我可以使用这个解决方案更轻松地构建我的 where 子句。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-12-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-02-01
相关资源
最近更新 更多