【问题标题】:Comparing different columns in SQL for each row比较 SQL 中每一行的不同列
【发布时间】:2017-06-18 18:39:47
【问题描述】:

经过一些转换后,我得到了一个交叉连接(来自表 a 和 b)的结果,我想对它进行一些分析。此表如下所示:

+-----+------+------+------+------+-----+------+------+------+------+
| id  | 10_1 | 10_2 | 11_1 | 11_2 | id  | 10_1 | 10_2 | 11_1 | 11_2 |
+-----+------+------+------+------+-----+------+------+------+------+
| 111 |    1 |    0 |    1 |    0 | 222 |    1 |    0 |    1 |    0 |
| 111 |    1 |    0 |    1 |    0 | 333 |    0 |    0 |    0 |    0 |
| 111 |    1 |    0 |    1 |    0 | 444 |    1 |    0 |    1 |    1 |
| 112 |    0 |    1 |    1 |    0 | 222 |    1 |    0 |    1 |    0 |
+-----+------+------+------+------+-----+------+------+------+------+

第一列的 id 与第六列的 id 不同。 一行中总是有两个相互匹配的不同 ID。其他列的值始终为 0 或 1。

我现在正试图找出两个 ID 平均共有多少个值(意味着在 10_1、10_2 等中都有“1”),但我真的不知道该怎么做。

我开始尝试这样的事情:

SELECT SUM(CASE WHEN a.10_1 = 1 AND b.10_1 = 1 then 1 end)

但这显然只会计算两个 id 共有 10_1 的频率。例如,我可以为不同的列做这样的事情:

SELECT SUM(CASE WHEN (a.10_1 = 1 AND b.10_1 = 1) 
OR (a.10_2 = 1 AND b.10_1 = 1) OR [...] then 1 end)

一般计算两个 ID 有一个共同点的频率,但如果它们有两个或多个共同点,这当然也可以计算在内。另外,我还想知道两个 IDS 有两件事、三件事等共同点的频率。

就我而言,一个“问题”也是我想查看大约 30 列,因此我几乎无法为每种情况写下所有可能的组合。

有谁知道我可以如何以更好的方式解决我的问题? 提前致谢。

编辑: 可能的结果如下所示:

+-----------+---------+
| in_common |  count  |
+-----------+---------+
|         0 |     100 |
|         1 |     500 |
|         2 |    1500 |
|         3 |    5000 |
|         4 |    3000 |
+-----------+---------+

【问题讨论】:

  • 编辑您的问题并显示您想要获得的结果。让我补充一点,CROSS JOIN 结果似乎对您的目标没有帮助。为什么不删除这个问题。用您的实际数据提出另一个问题,并清楚地解释您要做什么。
  • 我添加了一个可能的结果。我之前进行交叉连接的原因如下:我有两个子集(如男性和女性),我想根据某些标准相互匹配 - 我使用交叉连接和一些 where 子句来做到这一点。在此之后,我得到了如上所示的中间结果,我想知道男性和女性有多少共同的价值观,它们是匹配的,平均有多少共同点。
  • 您的数据在转换前是什么样子的?如果您的数据表有两列,一列用于 id,一列用于代码(10_1、10_2 等),则解决方案会更直接
  • 转换前的数据与上表类似。一列有一个 id,每个代码大约 30-40 列(10_1 等),以及其他几列,如年龄、性别等。从后者我使用一些列(如性别、年龄)进行匹配。男性和女性的两个子集都来自该表。

标签: sql postgresql


【解决方案1】:

使用代码作为列名,您将不得不编写一些明确引用每个列名的代码。为了将其保持在最低限度,您可以将这些引用写在一个对数据进行规范化的联合语句中,例如:

select id, '10_1' where "10_1" = 1
union
select id, '10_2' where "10_2" = 1
union
select id, '11_1' where "11_1" = 1
union
select id, '11_2' where "11_2" = 1;

这需要修改以包含链接不同 ID 所需的任何其他列。出于说明的目的,我假设以下数据模型

create table p (
    id integer not null primary key,
    sex character(1) not null,
    age integer not null
    );

create table t1 (
    id integer not null,
    code character varying(4) not null,
    constraint pk_t1 primary key (id, code)
    );

虽然您的数据目前显然不类似于这种结构,但将您的数据规范化为这样的形式将允许您应用以下解决方案以所需的形式汇总您的数据。

select
    in_common,
    count(*) as count
from (
    select
        count(*) as in_common
    from (
        select
        a.id as a_id, a.code,
        b.id as b_id, b.code
        from
        (select p.*, t1.code
            from p left join t1 on p.id=t1.id
            ) as a
        inner join (select p.*, t1.code
            from p left join t1 on p.id=t1.id
            ) as b on b.sex <> a.sex and b.age between a.age-10 and a.age+10
        where
        a.id < b.id
        and a.code = b.code
        ) as c
    group by
        a_id, b_id
    ) as summ
group by
    in_common;

【讨论】:

    【解决方案2】:

    建议的解决方案首先需要从交叉连接表中后退一步,因为相同的列名非常烦人。相反,我们从两个表中取出ids 并将它们放在一个临时表中。以下查询获得问题中想要的结果。它假设问题中的table_atable_b 相同并称为tbl,但不需要此假设并且tbl 可以在两个子SELECT 查询中替换为table_atable_b。它看起来很复杂,并使用 JSON 技巧来展平列,但它在这里工作:

    WITH idtable AS (
    SELECT a.id as id_1, b.id as id_2 FROM
       -- put cross join of table a and table b here
    )
    SELECT in_common,
           count(*)
    FROM
      (SELECT idtable.*,
              sum(CASE
                      WHEN meltedR.value::text=meltedL.value::text THEN 1
                      ELSE 0
                  END) AS in_common
       FROM idtable
       JOIN
         (SELECT tbl.id,
                 b.*
          FROM tbl,                         -- change here to table_a
               json_each(row_to_json(tbl)) b          -- and here too
          WHERE KEY<>'id' ) meltedL ON (idtable.id_1 = meltedL.id)
       JOIN
         (SELECT tbl.id,
                 b.*
          FROM tbl,                         -- change here to table_b
               json_each(row_to_json(tbl)) b          -- and here too
          WHERE KEY<>'id' ) meltedR ON (idtable.id_2 = meltedR.id
                                        AND meltedL.key = meltedR.key)
       GROUP BY idtable.id_1,
                idtable.id_2) tt
    GROUP BY in_common ORDER BY in_common;
    

    这里的输出如下所示:

     in_common | count 
    -----------+-------
             2 |     2
             3 |     1
             4 |     1
    (3 rows)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-04-30
      • 2019-08-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-10-31
      相关资源
      最近更新 更多