我有一个工作查询,现在很好 - 几乎就是我正在寻找的。但是,我想咨询一下,这是否是操纵我的数据以使其吐出我需要的最明智的方式:
我有一个存储报告数据的表REPORTS
。运行报表时会插入一行,而确认报表时会插入另一行。确认报告只需插入保留名称TRUE
,其日期与要确认的报告相同。丑陋,是的。但不幸的是,我不应该决定......
表格结构:
Reports
UID (char)
Report (char)
Date (date)
在运行报告后,表格REPORTS
可能看起来像这样:
+------+--------+---------------------+
| UID | Report | Date |
+------+--------+---------------------+
| 0001 | runX | 2014-01-02 03:04:59 |
| 0001 | runY | 2014-01-02 03:05:58 |
| 0001 | runX | 2014-01-02 03:06:20 |
+------+--------+---------------------+
在操作'报告确认'上,将插入以下行:
+------+--------+---------------------+
| UID | Report | Date |
+------+--------+---------------------+
| 0001 | TRUE | 2014-01-02 03:04:59 |
| 0001 | TRUE | 2014-01-02 03:05:58 |
| 0001 | TRUE | 2014-01-02 03:06:20 |
+------+--------+---------------------+
如您所见,当报告标记为TRUE
(即正确)时,有两行具有完全相同的DATE
:
+------+--------+---------------------+
| UID | Report | Date |
+------+--------+---------------------+
| 0001 | runX | 2014-01-02 03:04:59 |
| 0001 | TRUE | 2014-01-02 03:04:59 |
| 0001 | runY | 2014-01-02 03:05:58 |
| 0001 | TRUE | 2014-01-02 03:05:58 |
| 0001 | runX | 2014-01-02 03:06:20 |
| 0001 | TRUE | 2014-01-02 03:06:20 |
+------+--------+---------------------+
要返回所有“正确”的报告,即TRUE和相同的日期/时间报告名称,例如'runX',我会执行以下操作:
SELECT * FROM REPORTS T1
LEFT JOIN REPORTS T2
ON T1.DATE = T2.DATE
WHERE T1.REPORT = 'TRUE'
AND T1.REPORT != T2.REPORT;
这给了我至少可以使用的东西。但是,我知道必须是一种更优雅的方式吗?最后一个条款,例如:没有把它放进去,它吐了一个笛卡尔积,这意味着我创造了一个笛卡尔积,然后过滤它。据推测,必须有一种方法可以完全避免它,而不是首先创造它?
答案 0 :(得分:2)
如果我理解正确,您希望在TRUE
记录的同时从记录中提取名称,并且仅返回实际拥有TRUE
记录的报告:
select uid,
max(case when Report <> 'TRUE' then Report end) as Report,
date
from reports r
group by uid, date
having sum(case when Report = 'TRUE' then 1 else 0 end) > 0;
注意:使用时间组件对日期进行相等比较似乎很危险。创建这些表的过程应该是在记录中添加一些其他链接到正确的报告。例如,它可以更新检查标志列而不是创建新行。
编辑:
为什么加入日期(有时间)是个坏主意?通常,日期仅显示日期,没有时间组件。这意味着两个日期在输出中看起来可能相同,但实际上是不同的。或者,两个日期可以在不同的时区,看起来不同但是相同。
Oracle通过以精确格式存储日期到第二个问题来缓解第一个问题。与第二个看起来相同的两个日期是相同的。其他数据库中的等效数据类型有时包括毫秒 - 尽管很少使用该值打印出来。两次约会的次数可能看起来相同但仍然不同。在Oracle中,你可以说两个日期与时间到一分钟的日期可能看起来相同但仍然不同。
浮点数据类型出现相同的现象 - 1.0000000和0.9999999不同,但显示为1.000
时看起来相同。即使查看值会表明它会成功,但对这些值的连接也会失败。
答案 1 :(得分:0)
Oracle 11g R2架构设置:
CREATE TABLE reports ( "UID", Report, "Date" ) AS
SELECT '0001', 'runX', TO_DATE( '2014-01-02 03:04:59', 'yyyy-mm-dd hh24:mi:ss' ) FROM DUAL
UNION ALL SELECT '0001', 'TRUE', TO_DATE( '2014-01-02 03:04:59', 'yyyy-mm-dd hh24:mi:ss' ) FROM DUAL
UNION ALL SELECT '0001', 'runY', TO_DATE( '2014-01-02 03:05:58', 'yyyy-mm-dd hh24:mi:ss' ) FROM DUAL
UNION ALL SELECT '0001', 'TRUE', TO_DATE( '2014-01-02 03:05:58', 'yyyy-mm-dd hh24:mi:ss' ) FROM DUAL
UNION ALL SELECT '0001', 'runX', TO_DATE( '2014-01-02 03:06:20', 'yyyy-mm-dd hh24:mi:ss' ) FROM DUAL
UNION ALL SELECT '0001', 'TRUE', TO_DATE( '2014-01-02 03:06:20', 'yyyy-mm-dd hh24:mi:ss' ) FROM DUAL;
查询1 :
SELECT "UID",
MAX( CASE Report WHEN 'TRUE' THEN NULL ELSE Report END ) AS Report,
"Date"
FROM reports
GROUP BY "UID", "Date"
HAVING MAX( CASE Report WHEN 'TRUE' THEN 1 ELSE 0 END ) = 1
<强> Results 强>:
| UID | REPORT | DATE |
|------|--------|--------------------------------|
| 0001 | runX | January, 02 2014 03:04:59+0000 |
| 0001 | runY | January, 02 2014 03:05:58+0000 |
| 0001 | runX | January, 02 2014 03:06:20+0000 |
查询2 :
假设当报告标记为FALSE
时,如果报告不正确,则可以执行以下操作:
SELECT "UID",
Report,
"Date"
FROM ( SELECT "UID",
Report,
LEAD( Report )
OVER (
PARTITION BY "UID", "Date"
ORDER BY CASE Report
WHEN 'TRUE' THEN 2
WHEN 'FALSE' THEN 1
ELSE 0
END ) AS Result,
"Date"
FROM Reports )
WHERE Result = 'TRUE'
<强> Results 强>:
| UID | REPORT | DATE |
|------|--------|--------------------------------|
| 0001 | runX | January, 02 2014 03:04:59+0000 |
| 0001 | runY | January, 02 2014 03:05:58+0000 |
| 0001 | runX | January, 02 2014 03:06:20+0000 |