如何编写 SQL 为特定条件选择不同对值的查询?
How to write SQL query that selects distinct pair values for specific criteria?
我在为以下问题制定查询时遇到问题:
对于具有特定分数的对值,您如何以仅return 不同的具有最佳分数的对值的方式对它们进行分组?
例如,假设我有一个具有以下行值的 table:
(t1,p1,65)
(t1,p2,60)
(t1,p3,20)
(t2,p1,60)
(t2,p2,59)
(t2,p3,15)
前两列表示对值,第三列表示对 score.The 最佳得分是 (t1,p1,65)
。由于现在使用了 t1 和 p1,我想将它们排除在进一步分析之外。
下一个最好成绩是 (t2,p2,59)
。尽管 (t1,p2)
的分数为 60,但我想将其排除,因为 "t1" 已被使用。 (t2,p1)
也是60分,但是因为p1也已经被使用了,所以排除这一对。
这导致不同的对得分值:
(t1,p1,65)
(t2,p2,59)
有什么方法可以仅通过查询生成此结果吗?我试图想出对结果进行分组和分区的方法,但是由于必须根据分数排名对已经使用的值进行一些计算,我发现这很难实现。
编辑:
生成数据:
with t(t, p, score) as (
(values ('t1','p1',65),
('t1','p2',60),
('t1','p3',20),
('t2','p1',60),
('t2','p2',59),
('t2','p3',15)
))
select t.* from t;
如果第一对值和第二对值是不同的列(例如,X 和 Y),您可以按 X 分组并将 MAX(score) 作为聚合函数来获取以 X 开头的元组的最大分数。
进一步的步骤取决于您的数据,因为如果每个元组都被反转,您可能仍然会得到不需要的重复项。因此,要排除此类反向元组,您可以先进行自连接。
t1 已被使用,因此您已排除 (t1,p2),但 p1 也已被使用,您并未排除它。对我来说,它看起来只是按第一列分组。
select t1.c1, t2.c2, t1.s
from table1 t2
inner join (select c1, max(score) s from table1 group by t1) t1
on (t1.s=t2.score and t1.c1=t2.c1);
其中 table1
是您的 table 的名称,c1
是第一列,c2
第二列,score
第三列;
这个问题显然一直困扰着我。以下似乎实现了您的逻辑,将访问值的数组保持在行中:
with recursive t(t, p, score) as (
(values ('t1','p1',65),
('t1','p2',60),
('t1','p3',20),
('t2','p1',60),
('t2','p2',59),
('t2','p3',15)
)),
cte(t, p, score, cnt, lastt, lastp, ts, ps) as (
(select t.*, count(*) over ()::int, tt.t, tt.p, ARRAY[tt.t], ARRAY[tt.p]
from t cross join
(select t.* from t order by score desc limit 1) tt
)
union all
select t, p, score,
sum(case when not (ts @> ARRAY[t] or ps @> ARRAY[p]) then 1 else 0 end) over ()::int,
first_value(t) over (order by case when not (ts @> ARRAY[t] or ps @> ARRAY[p]) then score end desc nulls last),
first_value(p) over (order by case when not (ts @> ARRAY[t] or ps @> ARRAY[p]) then score end desc nulls last),
ts || first_value(t) over (order by case when not (ts @> ARRAY[t] or ps @> ARRAY[p]) then score end desc nulls last),
ps || first_value(p) over (order by case when not (ts @> ARRAY[t] or ps @> ARRAY[p]) then score end desc nulls last)
from cte
where cnt > 0
)
select *
from cte
where lastt = t and lastp = p and cnt > 0;
使用存储函数比较简单:
--drop function if exists f();
--drop table if exists t;
create table t(x text,y text, z int);
insert into t values
('t1','p1',65),
('t1','p2',60),
('t1','p3',20),
('t2','p1',60),
('t2','p2',59),
('t2','p3',15)/*,
('t3','p1',20),
('t3','p2',60),
('t3','p3',40)*/;
create function f() returns setof t immutable language plpgsql as $$
declare
ax text[];
ay text[];
r t;
begin
ax := '{}'; ay := '{}';
loop
select * into r
from t
where x <> all(ax) and y <> all(ay)
order by z desc, x, y limit 1;
exit when not found;
ax := ax || r.x; ay := ay || r.y;
return next r;
end loop;
end $$;
select * from f();
╔════╤════╤════╗
║ x │ y │ z ║
╠════╪════╪════╣
║ t1 │ p1 │ 65 ║
║ t2 │ p2 │ 59 ║
╚════╧════╧════╝
但是,如果取消对第三组值的注释,结果将不同:
╔════╤════╤════╗
║ x │ y │ z ║
╠════╪════╪════╣
║ t1 │ p1 │ 65 ║
║ t3 │ p2 │ 60 ║
║ t2 │ p3 │ 15 ║
╚════╧════╧════╝
更新:以及对相同测试数据使用递归 CTE 的等价物:
with recursive r as (
(select x, y, z, array[x] as ax, array[y] as ay from t order by z desc, x, y limit 1)
union all
(select t.x, t.y, t.z, r.ax || t.x, r.ay || t.y from t, r
where not (t.x = any(r.ax) or t.y = any(r.ay))
order by t.z desc, t.x, t.y limit 1))
select * from r;
我在为以下问题制定查询时遇到问题:
对于具有特定分数的对值,您如何以仅return 不同的具有最佳分数的对值的方式对它们进行分组?
例如,假设我有一个具有以下行值的 table:
(t1,p1,65)
(t1,p2,60)
(t1,p3,20)
(t2,p1,60)
(t2,p2,59)
(t2,p3,15)
前两列表示对值,第三列表示对 score.The 最佳得分是 (t1,p1,65)
。由于现在使用了 t1 和 p1,我想将它们排除在进一步分析之外。
下一个最好成绩是 (t2,p2,59)
。尽管 (t1,p2)
的分数为 60,但我想将其排除,因为 "t1" 已被使用。 (t2,p1)
也是60分,但是因为p1也已经被使用了,所以排除这一对。
这导致不同的对得分值:
(t1,p1,65)
(t2,p2,59)
有什么方法可以仅通过查询生成此结果吗?我试图想出对结果进行分组和分区的方法,但是由于必须根据分数排名对已经使用的值进行一些计算,我发现这很难实现。
编辑:
生成数据:
with t(t, p, score) as (
(values ('t1','p1',65),
('t1','p2',60),
('t1','p3',20),
('t2','p1',60),
('t2','p2',59),
('t2','p3',15)
))
select t.* from t;
如果第一对值和第二对值是不同的列(例如,X 和 Y),您可以按 X 分组并将 MAX(score) 作为聚合函数来获取以 X 开头的元组的最大分数。
进一步的步骤取决于您的数据,因为如果每个元组都被反转,您可能仍然会得到不需要的重复项。因此,要排除此类反向元组,您可以先进行自连接。
t1 已被使用,因此您已排除 (t1,p2),但 p1 也已被使用,您并未排除它。对我来说,它看起来只是按第一列分组。
select t1.c1, t2.c2, t1.s
from table1 t2
inner join (select c1, max(score) s from table1 group by t1) t1
on (t1.s=t2.score and t1.c1=t2.c1);
其中 table1
是您的 table 的名称,c1
是第一列,c2
第二列,score
第三列;
这个问题显然一直困扰着我。以下似乎实现了您的逻辑,将访问值的数组保持在行中:
with recursive t(t, p, score) as (
(values ('t1','p1',65),
('t1','p2',60),
('t1','p3',20),
('t2','p1',60),
('t2','p2',59),
('t2','p3',15)
)),
cte(t, p, score, cnt, lastt, lastp, ts, ps) as (
(select t.*, count(*) over ()::int, tt.t, tt.p, ARRAY[tt.t], ARRAY[tt.p]
from t cross join
(select t.* from t order by score desc limit 1) tt
)
union all
select t, p, score,
sum(case when not (ts @> ARRAY[t] or ps @> ARRAY[p]) then 1 else 0 end) over ()::int,
first_value(t) over (order by case when not (ts @> ARRAY[t] or ps @> ARRAY[p]) then score end desc nulls last),
first_value(p) over (order by case when not (ts @> ARRAY[t] or ps @> ARRAY[p]) then score end desc nulls last),
ts || first_value(t) over (order by case when not (ts @> ARRAY[t] or ps @> ARRAY[p]) then score end desc nulls last),
ps || first_value(p) over (order by case when not (ts @> ARRAY[t] or ps @> ARRAY[p]) then score end desc nulls last)
from cte
where cnt > 0
)
select *
from cte
where lastt = t and lastp = p and cnt > 0;
使用存储函数比较简单:
--drop function if exists f();
--drop table if exists t;
create table t(x text,y text, z int);
insert into t values
('t1','p1',65),
('t1','p2',60),
('t1','p3',20),
('t2','p1',60),
('t2','p2',59),
('t2','p3',15)/*,
('t3','p1',20),
('t3','p2',60),
('t3','p3',40)*/;
create function f() returns setof t immutable language plpgsql as $$
declare
ax text[];
ay text[];
r t;
begin
ax := '{}'; ay := '{}';
loop
select * into r
from t
where x <> all(ax) and y <> all(ay)
order by z desc, x, y limit 1;
exit when not found;
ax := ax || r.x; ay := ay || r.y;
return next r;
end loop;
end $$;
select * from f();
╔════╤════╤════╗
║ x │ y │ z ║
╠════╪════╪════╣
║ t1 │ p1 │ 65 ║
║ t2 │ p2 │ 59 ║
╚════╧════╧════╝
但是,如果取消对第三组值的注释,结果将不同:
╔════╤════╤════╗
║ x │ y │ z ║
╠════╪════╪════╣
║ t1 │ p1 │ 65 ║
║ t3 │ p2 │ 60 ║
║ t2 │ p3 │ 15 ║
╚════╧════╧════╝
更新:以及对相同测试数据使用递归 CTE 的等价物:
with recursive r as (
(select x, y, z, array[x] as ax, array[y] as ay from t order by z desc, x, y limit 1)
union all
(select t.x, t.y, t.z, r.ax || t.x, r.ay || t.y from t, r
where not (t.x = any(r.ax) or t.y = any(r.ay))
order by t.z desc, t.x, t.y limit 1))
select * from r;