Sql 限制2元组中元素的出现次数

Sql 限制2元组中元素的出现次数,sql,postgresql,postgresql-9.4,Sql,Postgresql,Postgresql 9.4,我正在尝试在SQL或postgresql 9.4中找到一个基于集合的查询解决方案,以解决以下问题: 我有一个唯一的2元组x的有限集∈ N、 y∈ N具有指定的秩 现在我想删除所有元组,以便剩余的元组满足以下条件: 每个数字在元组左侧最多出现n次,并且 每个数字在右侧最多出现m次。 这对于迭代有序元组并计算每个元素的出现次数的过程来说很容易做到。然而,我想知道是否有一个单一的postgreSQL查询解决方案 更具体地说,请考虑下面的简单例子,n=2,m=2: ╔═══╦═══╦══════╗ ║

我正在尝试在SQL或postgresql 9.4中找到一个基于集合的查询解决方案,以解决以下问题:

我有一个唯一的2元组x的有限集∈ N、 y∈ N具有指定的秩

现在我想删除所有元组,以便剩余的元组满足以下条件:

每个数字在元组左侧最多出现n次,并且 每个数字在右侧最多出现m次。 这对于迭代有序元组并计算每个元素的出现次数的过程来说很容易做到。然而,我想知道是否有一个单一的postgreSQL查询解决方案

更具体地说,请考虑下面的简单例子,n=2,m=2:

╔═══╦═══╦══════╗
║ x ║ y ║ rank ║
╠═══╬═══╬══════╣
║ 1 ║ 4 ║    1 ║
║ 2 ║ 4 ║    2 ║
║ 3 ║ 4 ║    3 ║
║ 3 ║ 5 ║    4 ║
║ 3 ║ 6 ║    5 ║
║ 3 ║ 7 ║    6 ║
╚═══╩═══╩══════╝
现在我们正在寻找一个返回以下元组作为结果的查询:1,4,2,4,3,5,3,6

表和值的SQL FIDLE:

   create table tab (
     x bigint,
     y bigint,
     rank bigint);

  insert into tab values (1,4,1);
  insert into tab values (2,4,2);
  insert into tab values (3,4,3);
  insert into tab values (3,5,4);
  insert into tab values (3,6,5);
  insert into tab values (3,7,6);
我尝试了一种使用postgres窗口函数的方法,它解决了上面的示例,但我不确定它是否可以找到与其他示例中基于光标的方法一样多的对

    SELECT x, y FROM (
      SELECT x, y, ROW_NUMBER() OVER (PARTITION BY x ORDER BY rank) AS rx FROM (
        SELECT x, y, rank, ROW_NUMBER() OVER (PARTITION BY y ORDER BY rank) AS ry FROM tab) AS limitY
      WHERE limitY.ry < 3) AS limitX
    WHERE limitX.rx < 3

这是一种使用单个窗口功能的变体,通过可能更快:

select x, y, rank
from (
  select *, count(*) over (partition by x order by rank) as cx,
            count(*) over (partition by y order by rank) as cy
  from tab
  order by rank
  ) t
where cx < 3 and cy < 3;
还有递归CTE方法:

-- use tab directly instead of tabr CTE (and replace all ocurrences of r column with rank)
-- if rank is trusted to be sequential uninterrupted starting with 1
with recursive
  r (r, x, y, rank, cx, cy) as (
    select *, 1 as cx, 1 as cy
    from tabr where r = 1
    union all
    select t.*, case when r.x = t.x then r.cx + 1 else 1 end as cx, case when r.y = t.y then r.cy + 1 else 1 end as cy
    from r, tabr t
    where t.r = r.r + 1
    ),
  tabr as (
    select row_number() over () as r, *
    from tab
    order by rank
    )
select x, y, rank
from r
where cx <= 2 and cy <= 2
order by r;

这一次花了一些时间,但我在MS SQL Server中找到了一个解决方案,我认为应该将其转换为PostGreSQL。SQLServer对递归CTE中的内容有一些限制,我不完全知道PostGreSQL有哪些限制。也就是说,希望这对你有用,或者为你指明了正确的方向

棘手的是,被排除的行会根据已经被排除的行进行更改,因此无法简单地计算它们,因为它们同时依赖于x和y,递归CTE不能按顺序构建,因为它只能引用自身一次。就在那时,我想出了将计数嵌入字符串的想法。这根本不能很好地扩展-例如,如果在排除一行之前规则更改为3或4个实例,那么CASE语句将开始爆炸

WITH CTE_Excludes AS
(
    SELECT
        x,
        y,
        [rank],
        CAST('|' + CAST(x AS VARCHAR(4)) + '-1|' AS VARCHAR(1000)) AS x_counts,
        CAST('|' + CAST(y AS VARCHAR(4)) + '-1|' AS VARCHAR(1000)) AS y_counts,
        0 AS excluded
    FROM
        tab
    WHERE
        [rank] = 1
    UNION ALL
    SELECT
        T.x,
        T.y,
        T.[rank],
        CAST(CASE
            WHEN X.x_counts LIKE '%|' + CAST(T.x AS VARCHAR(4)) + '-2|%' OR X.y_counts LIKE '%|' + CAST(T.y AS VARCHAR(4)) + '-2|%' THEN X.x_counts
            WHEN X.x_counts LIKE '%|' + CAST(T.x AS VARCHAR(4)) + '-1|%' THEN REPLACE(X.x_counts, '|' + CAST(T.x AS VARCHAR(4)) + '-1|', '|' + CAST(T.x AS VARCHAR(4)) + '-2|')
            ELSE X.x_counts + '|' + CAST(T.x AS VARCHAR(4)) + '-1|'
        END AS VARCHAR(1000)) AS x_counts,
        CAST(CASE
            WHEN X.x_counts LIKE '%|' + CAST(T.x AS VARCHAR(4)) + '-2|%' OR X.y_counts LIKE '%|' + CAST(T.y AS VARCHAR(4)) + '-2|%' THEN X.y_counts
            WHEN X.y_counts LIKE '%|' + CAST(T.y AS VARCHAR(4)) + '-1|%' THEN REPLACE(X.y_counts, '|' + CAST(T.y AS VARCHAR(4)) + '-1|', '|' + CAST(T.y AS VARCHAR(4)) + '-2|')
            ELSE X.y_counts + '|' + CAST(T.y AS VARCHAR(4)) + '-1|'
        END AS VARCHAR(1000)) AS y_counts,
        CASE
            WHEN X.x_counts LIKE '%|' + CAST(T.x AS VARCHAR(4)) + '-2|%' OR X.y_counts LIKE '%|' + CAST(T.y AS VARCHAR(4)) + '-2|%' THEN 1
            ELSE 0
        END AS excluded
    FROM
        CTE_Excludes X
    INNER JOIN tab T ON T.[rank] = X.[rank] + 1
)
SELECT
    x, y
FROM
    CTE_Excludes
WHERE
    excluded = 0

当你说“声明性”时,你是指单个基于集合的查询吗?SQL上下文中的声明性通常意味着声明性RI,这将是一件非常不同的事情。是的,我指的是基于单个集合的查询。我正在寻找一种遵循声明式编程方法的解决方案,而不是使用存储过程的命令式解决方案。当按秩排序时,x或y中是否有元素顺序不正确?如果不是,可以相信秩是完全连续的吗?你可以假设x和y总是按秩升序排列,它们的值是按顺序排列的。为了保持示例的简单性,这里只是巧合,然而,我在这个问题上做了更多的工作,需要在最初的2中添加另一个条件,最大化返回的对的数量,这将显著改变范围。我应该编辑我的原始帖子还是创建一个后续问题?您的第一个解决方案考虑到3,6将是有效的,因为3,4已经因为y而被排除,所以x=3的计数会发生变化。我不知道第二个解决方案是否有相同的错误。