我有一个数据库表,其中包含有关客户可用补丁的数据。服务器。有两列,identifier
是每个补丁的标识符,date
是该数据的插入日期。
我想比较过去一周的标识符与当前周的标识符。如果标识符存在于上周的日期并且不再存在,则表示该修补程序已应用。如果它存在于当前周,而不是过去一周,那就意味着它是一个新的补丁。我想破译哪些补丁是新的,哪些已安装。
为此,我构建了两个查询,如下所示:
SELECT `identifier`AS 'id1', `date` AS 'data1' FROM `patches` WHERE `date`="2015-02-02";
SELECT `identifier` AS 'id2', `date` AS 'data2' FROM `patches` WHERE `date`="2015-02-09"
我需要编写某种联接来检索已安装或已经安装标识符的数据,以及每个数据的计数。上述两个查询都有效,但我需要比较它们的结果。
我使用的列如下所示:
| date | identifier |
+--------+--------------------+
| 2/2/15 | 192.168.0.25-0001 |
| 2/2/15 | 192.168.0.77-1457 |
| 2/2/15 | 192.168.0.123-1329 |
| 2/2/15 | 192.168.0.84-2214 |
| 2/9/15 | 192.168.0.25-0001 |
| 2/9/15 | 192.168.0.77-1457 |
| 2/9/15 | 192.168.0.44-5311 |
| 2/9/15 | 192.168.0.78-1384 |
目前,我必须在Excel中进行查找并将其与CSV进行比较。如果标识符之前存在且不再存在,则excel会在单元格中添加#N / D,因此我会计算出#N / D'#N / D'单元格可以获得应用的补丁数量。如何在SQL中获取此信息?
更新:所以,我测试了shawnt00的答案和McAdam331的答案,他们两个都有效。但现在我有另一个与此问题相关的问题:
在我的工作中,我们每周都会制作这些补丁报告。因此,每周都有新的补丁和应用补丁。
我正在构建一个网页,用户(我们的员工)可以登录,选择一个客户,初始日期和最终日期,并通过ajax加载请求的数据。
一切运行良好,网页完成,登录系统也是如此。
问题是:
如果用户仅选择2个日期(初始和最终),我如何获得"中间"中的数据信息。这两个日期?
例如,让我们说用户登录并选择日期" 2015-02-02"和" 2015-02-23"。
假设数据库包含两个日期以及它们之间的日期的数据,这些数据是" 2015-02-09"和" 2015-02-16",我怎样才能使用这些查询,你们让我们进行同样的比较,但每周一次?
在上面的示例中,我需要在" 2015-02-02"之间获取新的和应用的补丁的数量。和" 2015-02-09",然后" 2015-02-09"和" 2015-02-16",然后" 2015-02-16"和" 2015-02-23"最后" 2015-03-02",而不是" 2015-02-02"和" 2015-03-02"。
我尝试使用php创建某种循环来遍历mysql中的日期和foreach
日期我运行查询并总结我得到的数字,以便在每种情况下显示最终计数,但它没有& #39;工作。
有人能帮助我吗?
答案 0 :(得分:2)
对日期进行硬编码是不理想的,并且使这种动态变得非常容易(比如相对于当前日期)。我想这会回答你对你感兴趣的几个星期的问题。
SELECT
identifier,
case
when count(`date`) = 2 then 'Not applied'
when max(`date`) = '2015-02-09' /* and count(`date`) = 1 */ then 'New patch'
when min(`date`) = '2015-02-02' /* and count(`date`) = 1 */ then 'Applied'
end as `status`
FROM patches
WHERE `date` IN ('2015-02-02', '2015-02-09')
GROUP BY identifier
总结也很简单:
SELECT `status`, count(*) FROM (
SELECT
identifier,
case
when count(`date`) = 2 then 'Not applied'
when max(`date`) = '2015-02-09' then 'New patch'
when min(`date`) = '2015-02-02' then 'Applied'
end as `status`
FROM patches
WHERE `date` IN ('2015-02-02', '2015-02-09')
GROUP BY identifier
) as T
GROUP BY `status`
如果你没有将这个日期分摊超过52周,这可能会有效。我担心用户的日期选择与周一日期所代表的周选择相匹配。
SELECT
identifier,
case
when week(min(`date`)) = week(:end) then 'New patch'
when week(max(`date`)) = week(:end) then 'Not applied'
when week(max(`date`)) < week(:end) then 'Applied'
end as `status`
/* -- This might better work for all dates
when min(`date`)) = date_sub(:end, mod(dayofweek(:end) + 5, 7) day)
then 'New patch'
when max(`date`)) = date_sub(:end, mod(dayofweek(:end) + 5, 7) day)
then 'Not applied'
when max(`date`)) < date_sub(:end, mod(dayofweek(:end) + 5, 7) day)
then 'Applied'
*/
FROM patches
WHERE `date` BETWEEN :start and :end
GROUP BY identifier
这是一周一周的结果......
SELECT
p.identifier,
p.`date`,
sum(case when pb.`date` is null and p.`date` < max_date then 1 else 0) as new
sum(case when pf.`date` is null and p.`date` > min_date then 1 else 0) as applied
FROM
patches as p
left outer join patches as pb
on pb.identifier = p.identifier and pb.`date` = date_sub(p.`date`, 7 day)
left outer join patches as pf
on pf.identifier = p.identifier and pf.`date` = date_add(p.`date`, 7 day)
cross join
(select min(`date`) as min_date, max(`date`) as max_date from patches) as rng
WHERE p.`date` BETWEEN :start and :end
GROUP BY p.identifier, p.`date`
答案 1 :(得分:0)
有更清洁/更严格的方法,但只是将您的查询投入LEFT OUTER JOIN并仅选择上周出现的记录,但不是本周,您会得到:
SELECT
`id1`
FROM
(SELECT `identifier`AS 'id1', `date` AS 'data1' FROM `patches` WHERE `date`="2015-02-02") last_week
LEFT OUTER JOIN (SELECT `identifier` AS 'id2', `date` AS 'data2' FROM `patches` WHERE `date`="2015-02-09") current_week ON
last_week.id1 = current_week.id2
WHERE
current_week.id2 IS NULL
更新:清除了一点以删除第一个派生表。这应该在MySQL中具有相同的性能,因为执行路径很可能完全相同。虽然,我已经看到MySQL做出了一些奇怪的决定,所以YMMV:
SELECT
`id1`
FROM
`patches` last_week
LEFT OUTER JOIN (SELECT `identifier` AS 'id2', `date` AS 'data2' FROM `patches` WHERE `date`="2015-02-09") current_week ON
last_week.identifier = current_week.id2
WHERE
last_week.`date` = "2015-02-02"
current_week.id2 IS NULL
此外,既然你是从Excel / Vlookup思维模式来看这个,你就可以这样想。如果您将Vlookup放在最后几周的数据上并查找当前周的数据,然后查找#N / A记录,那么这基本上就是您从上述查询中得到的结果。如果您只想要VLOOKUP在该场景中返回值的记录,那么您可以删除第一个查询中的WHERE条件并将JOIN更改为INNER JOIN(或将where条件更改为WHERE current_week IS NOT NULL
。
如果您切换它并将VLOOKUP放在CURRENT_WEEK上以查找不在前一周数据中的记录,那么只需在第一个查询中翻转FROM语句中的表。 CURRENT_WEEK LEFT OUTER JOIN LAST_WEEK,并更改WHERE以查找last_Week.id1 IS NULL。
答案 2 :(得分:0)
根据您的预期结果,就是这么简单:
SELECT `identifier`, `date` FROM `patches` WHERE `date`="2015-02-02"
UNION
SELECT `identifier`, `date` FROM `patches` WHERE `date`="2015-02-09"
但问题是,为什么你不这样做:
SELECT `identifier`, `date` FROM `patches` WHERE `date`="2015-02-02" OR `date`="2015-02-09";
我认为你错过了你的要求......
根据你的意见:-)试试这个:
SELECT `identifier`, `date`, COUNT(*) as `counter`
FROM `patches`
WHERE `date`="2015-02-02" OR `date`="2015-02-09"
GROUP BY `identifier`
HAVING (`counter`=1);
尝试更深入地解释你的目标。
答案 3 :(得分:0)
我建议使用NOT IN
运算符。您可以获取02/02上发生的所有行,而不是02/09上的行:
SELECT identifier, dateCol
FROM myTable
WHERE dateCol = '2015-02-02'
AND identifier NOT IN(
SELECT identifier
FROM myTable
WHERE dateCol = '2015-02-09');
要反过来,只需翻转日期即可。如果您想获得仅在第一周内的标识符计数,您可以在该子查询上使用COUNT()函数,并按日期分组:
SELECT dateCol, COUNT(*) AS numFixedPatches
FROM(
SELECT identifier, dateCol
FROM myTable
WHERE dateCol = '2015-02-02'
AND identifier NOT IN(
SELECT identifier
FROM myTable
WHERE dateCol = '2015-02-09')) tmp
GROUP BY dateCol;
以下是SQL Fiddle示例。
因此,相反的例子(获得新补丁)看起来像这样:
SELECT dateCol, COUNT(*) AS numNewPatches
FROM(
SELECT identifier, dateCol
FROM myTable
WHERE dateCol = '2015-02-09' AND identifier NOT IN(
SELECT identifier
FROM myTable
WHERE dateCol = '2015-02-02')) tmp
GROUP BY datecol;