SQL - 如何在不必传递select的所有列的情况下执行组操作?

Mat*_*ari 5 sql firebird

我有以下选择,其目标是选择自X天以来没有销售的所有客户,并且还提供上次销售的日期和销售数量:

select s.customerId, s.saleId, max (s.date) from sales s
group by s.customerId, s.saleId
having max(s.date) <= '05-16-2013'
Run Code Online (Sandbox Code Playgroud)

但这样它给我带来了以下内容:

19 | 300 | 26/09/2005
19 | 356 | 29/09/2005
27 | 842 | 10/05/2012
Run Code Online (Sandbox Code Playgroud)

换句话说,前两行是来自同一个客户(id 19),我希望他每个客户只能获得一条记录,这将是最大日期的记录,在这种情况下,第二条记录来自此名单.通过这个逻辑,我应该从"group by"子句中取消s.saleId,但是如果我这样做,当然,我得到错误:选择列表中的表达式无效(不包含在聚合函数或GROUP BY中)条款).

我正在使用firebird 1.5

我怎样才能做到这一点?

Mik*_*ll' 8

GROUP BY通过聚合一组行的,返回数据总结一个每组行.您正在使用聚合函数max(),该函数将从一组行的一列返回最大值.

我们来看一些数据吧.我重命名了您称为"日期"的列.

create table sales (
  customerId integer not null,
  saleId integer not null,
  saledate date not null
  );


insert into sales values
(1, 10, '2013-05-13'),
(1, 11, '2013-05-14'),
(1, 12, '2013-05-14'),
(1, 13, '2013-05-17'),
(2, 20, '2013-05-11'),
(2, 21, '2013-05-16'),
(2, 31, '2013-05-17'),
(2, 32, '2013-03-01'),
(3, 33, '2013-05-14'),
(3, 35, '2013-05-14');
Run Code Online (Sandbox Code Playgroud)

你说

换句话说,前两行是来自同一个客户(id 19),我希望他每个客户只能获得一条记录,这将是最大日期的记录,在这种情况下,第二条记录来自此名单.

select s.customerId, max (s.saledate) 
from sales s
where s.saledate <= '2013-05-16'
group by s.customerId
order by customerId;

customerId  max
--
1           2013-05-14 
2           2013-05-16
3           2013-05-14
Run Code Online (Sandbox Code Playgroud)

那张桌子是什么意思?这意味着5月16日或之前客户"1"购买东西的最新日期是5月14日; 客户"2"买入东西的5月16日或之前的最新日期是5月16日.如果在连接中使用此派生表,它将返回具有一致含义的可预测结果.

现在让我们看一个稍微不同的查询.MySQL允许这种语法,并返回下面的结果集.

select s.customerId, s.saleId, max(s.saledate) max_sale
from sales s
where s.saledate <= '2013-05-16'
group by s.customerId
order by customerId;

customerId  saleId  max_sale
--
1           10      2013-05-14
2           20      2013-05-16
3           33      2013-05-14
Run Code Online (Sandbox Code Playgroud)

ID为"10"的销售未在5月14日发生; 它发生在5月13日.这个问题产生了错误.将此派生表与销售事务表联系起来将使错误更加复杂.

这就是Firebird正确引发错误的原因.解决方案是从SELECT子句中删除saleId.

现在,说了这么多,你可以找到自5月16日以来没有销售的客户.

select distinct customerId from sales
where customerID not in
  (select customerId
  from sales
  where saledate >= '2013-05-16')
Run Code Online (Sandbox Code Playgroud)

你可以得到正确的customerId和"正确的"saleId这样的.(我说"正确"的saleId,因为当天可能有不止一个.我只选择了最大值.)

select sales.customerId, sales.saledate, max(saleId)
from sales
inner join (select customerId, max(saledate) max_date
            from sales
            where saledate < '2013-05-16'
            group by customerId) max_dates
        on sales.customerId = max_dates.customerId
        and sales.saledate = max_dates.max_date
inner join (select distinct customerId 
            from sales
            where customerID not in
              (select customerId
               from sales
               where saledate >= '2013-05-16')) no_sales
        on sales.customerId = no_sales.customerId
group by sales.customerId, sales.saledate
Run Code Online (Sandbox Code Playgroud)

就个人而言,我发现公共表表达式使我更容易读取类似的SQL语句而不会迷失在SELECT中.

with no_sales as (
  select distinct customerId 
  from sales
  where customerID not in
    (select customerId
     from sales
     where saledate >= '2013-05-16')
),
max_dates as (
  select customerId, max(saledate) max_date
  from sales
  where saledate < '2013-05-16'
  group by customerId
)
select sales.customerId, sales.saledate, max(saleId)
from sales
inner join max_dates
        on sales.customerId = max_dates.customerId
        and sales.saledate = max_dates.max_date
inner join no_sales
        on sales.customerId = no_sales.customerId
group by sales.customerId, sales.saledate
Run Code Online (Sandbox Code Playgroud)