【问题标题】:SQL query help with GROUPing and nested queriesGROUPing 和嵌套查询的 SQL 查询帮助
【发布时间】:2010-12-03 06:37:27
【问题描述】:

给定两张表,

  • A包含customeridlastchangeinternallink

  • B 包含internallinkturnover

(我这里只是简单的把它改成一个通用的例子,实际结构更复杂。SQL方言现在是mySQL。)

唯一独特的东西(每个表)是内部链接。 A 中有几条记录具有相同的 customerID、不同的 lastchange 日期和不同的 internallink 值。 还有其他与此相关的项目;我无法更改表格。

我需要来自 A 的记录 ID,这些记录 ID 是客户的最新记录(具有相同 customerID 的所有记录中的最高 lastchange 值) B 中的条目与某个值匹配条件已关联。

我觉得

SELECT `internallink` FROM `B` WHERE (`turnover` > 10000) 

部分不是问题。

我已经走到这一步了:

SELECT `customerID`, MAX(`lastchange`)
  FROM `A` 
 WHERE `lastchange` IN (SELECT `internallink` FROM `B` 
                         WHERE `turnover` > 10000)
 GROUP BY `customerID`;

唉,该语句给出了错误的结果,因为上面将返回我最近的值不满足条件的客户 ID,但一些较旧的值 - 它选择最旧的值,并返回这个。但如果最近的条目低于阈值,则 customerID 根本不应该出现。

我哪里出错了,正确的方法是什么?

样本数据 表A

customerid lastchange 内部链接 3 2010-02-11 11 3 2010-09-04 12 3 2010-10-22 13 3 2010-11-23 14 4 2010-05-05 15 4 2010-12-01 16 5 2010-11-28 17 5 2010-11-29 18

表 B

内部链接周转 11 47000 12 11000 13 8000 14 15000 15 17000 16 23000 17 50000 18 10000

我测试的实际阈值是 12000。 您可以看到 customerID 不应该出现在结果集中,因为最近的条目低于阈值。

结果集应该是 (3,2010-11-23)(4,2010-12-01) - 但目前它还包含 (5,2010-11-28),这是错误的。


靠近一点(在您的帮助下,谢谢!),这两个语句都有效:

SELECT customerID, MAX(lastchange), internallink FROM A GROUP BY customerID; SELECT internallink FROM B WHERE (营业额 > 12000);

现在我所需要的只是两者的交集......具有正确的逻辑!

【问题讨论】:

  • 有没有办法可以发布一些示例数据?每张表只有几条记录?用你写的东西很难想象问题。
  • 您可能对此stack-exchange proposal 感兴趣。它几乎可以开始测试了,只需要更多。

标签: sql group-by nested grouping query-help


【解决方案1】:

下面的查询应该做你想做的事。它不是编写这种查询的最佳方式。但它使用标准 SQL,可以在任何数据库中执行。

工作原理如下:内部子查询查找所有客户 ID 以及最新的更改日期。对于每个这样的对(customerid, lastchange),我们在表 A 中找到原始行。在表 A 中找到一行后,我们使用内部链接在 B 中查找匹配记录,但前提是关联的营业额大于 10000。

drop table a;
drop table b;

create table a(
   customerid   int  not null
  ,lastchange   date not null
  ,internallink int  not null
);

create table b(
   internallink int not null
  ,turnover     int not null
);

insert into a values(3, date '2010-02-11', 11);
insert into a values(3, date '2010-09-04', 12);
insert into a values(3, date '2010-10-22', 13);
insert into a values(3, date '2010-11-23', 14);
insert into a values(4, date '2010-05-05', 15);
insert into a values(4, date '2010-12-01', 16);
insert into a values(5, date '2010-11-28', 17);
insert into a values(5, date '2010-11-29', 18);

insert into b values(11, 47000);
insert into b values(12, 11000);
insert into b values(13,  8000);
insert into b values(14, 15000);
insert into b values(15, 17000);
insert into b values(16, 23000);
insert into b values(17, 50000);
insert into b values(18, 10000);

select a.customerid
      ,a.lastchange
      ,a.internallink
      ,b.turnover
  from a
  join b on (a.internallink = b.internallink)
 where b.turnover > 10000
   and (a.customerid, a.lastchange) in(select customerid,max(lastchange)
                                         from a
                                     group by customerid);

【讨论】:

  • 看起来不错,虽然它似乎切断了太多 - 但我目前的问题是:那么有更有效的方法吗?只是不是标准SQL?我遇到的问题是使用“IN”,我只想要一列,但有两列,没有办法选择只匹配一列。
【解决方案2】:

这适用于 sql server - 我不确定 mySql 是否有类似的排名功能。

select a.id, a.lastchange, b.turnover, a.rownumber from B b inner join 
(SELECT id, lastchange, internallink, ROW_NUMBER() OVER(PARTITION BY id ORDER BY lastchange DESC) AS 'rownumber'
FROM A) a on b.internallink = a.internallink
where a.rownumber = 1 and b.turnover > 5000

“ROW_NUMBER() OVER(PARTITION BY id ORDER BY lastchange DESC) AS 'rownumber'”表示...

我想将所有相同的 id 组合在一起,并在计算每一行之后按 lastchange by desc 对它们进行排序。哦,并命名该列的行号。

id  lastchange    internallink  rownumber
1   2010-01-03    2           1
1   2010-01-02    1           2
1   2010-01-01    1           3
2   2010-01-04    2           1

选择行号为 1 的任何记录将返回 id 的最后修改记录。

【讨论】:

  • @foo - MySQL 没有分析功能,但本文解释了如何伪造它们:explainextended.com/2009/03/08/…
  • 我需要一些时间才能完整地翻译/模拟这个,所以我可以测试它。并有更多时间先了解它。我希望有一种更简单的方法来做到这一点......
  • @foo - APC 提供的链接很有帮助。当你在消化它时,尝试不同的查询变体(改变什么被分区和/或什么被排序)并查看结果——它应该可以帮助你更快地理解事情。祝你好运。
  • 我不得不同意,那个链接非常有用,我可以推荐它!
【解决方案3】:

经过大量测试和研究,我找到了这个解决方案,并发布了 以防其他人遇到类似问题。

一个额外的表“缓存”保留了表 A 中最新条目的副本,从而大大降低了复杂性。它通过使用这样的触发器保持最新:

在 FOR EACH ROW 上插入后创建触发器 sync_a_insert 插入缓存(`customerID`,`internallink`)值(NEW.`customerID`,NEW.`internallink`); 在每行更新后创建触发器 sync_a_update 更新缓存 SET `internallink` = NEW.`internallink` WHERE (`customerID` = NEW.`customerID`); 在 FOR EACH ROW 上删除之前创建触发器 sync_a_delete 从缓存中删除 `customerID` = OLD.`customerID`;

对于 INSERT 和 UPDATE,这些触发器在事后触发,因此表 a 中的条目在缓存更新之前是完整的。对于 DELETE,需要在原始条目消失之前更新缓存。

一旦到位,其他一切都变得简单:

从缓存中选择 `customerID` WHERE `internallink` IN (SELECT `internallink` FROM b WHERE (`turnover` > 10000));

对我来说,这是一个可行的解决方案,它甚至可以加快查找速度。当然,DB 大小是有代价的,但我认为整体性能要好得多 - 只要读取访问权限比写入访问权限多至少一次,就会有所改进。

但是,您给出的答案对我很有帮助。我从他们那里学到了很多东西,并尝试遵循您的建议(甚至已经将其中一些用于其他地方)。感谢所有回答我问题的人!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多