【问题标题】:SQL: how to select the row with most known values?SQL:如何选择具有最多已知值的行?
【发布时间】:2012-04-20 08:48:00
【问题描述】:

我有一个用户表(用户名、性别、出生日期、zip),其中用户的 id 是永久的,但用户过去可以注册多次,有时他填写了所有数据,有时没有。除此之外,他还可以更改居住地(在这种情况下 zip 可以更改)。

所以查询

SELECT username, sex, date_birth, zip FROM users_log WHERE username IN('user1', 'user2', 'user3')

返回以下结果:

"user1";"M";"1982-10-04 00:00:00";"6320"
"user2";"";"";"1537"
"user3";"";"";"1537"
"user3";"";"";"1000"
"user3";"";"";"1000"
"user3";"";"1979-05-29 00:00:00";"1000"
"user3";"";"";"1537"
"user3";"";"1979-05-29 00:00:00";"1000"
"user1";"";"";"1000"
"user3";"";"";"1537"

在这种情况下,user1已经改变了住所;邮政编码已更改; “属于”他的第二行不包含人口统计数据。 User3 也有多条记录,只有两条记录包含人口统计数据。

我想做的是将用户与包含最多有关他的数据的行绑定,并考虑包含在具有最已知值的行中的 zip。有谁知道如何编写适当的查询?

谢谢!

【问题讨论】:

  • 您能否向我们提供您排除的确切数据?

标签: sql postgresql deduplication


【解决方案1】:

这会很痛苦;非常痛苦。

您的问题并不清楚这个问题,但我假设您所指的“用户 ID”是用户名。如果有错误,需要进行相应的修改。

与任何复杂的查询一样,分阶段构建它。

第一阶段:每条记录有多少个非空字段?

SELECT username, sex, date_of_birth, zip,
       CASE WHEN sex           IS NULL THEN 0 ELSE 1 END +
       CASE WHEN date_of_birth IS NULL THEN 0 ELSE 1 END +
       CASE WHEN zip           IS NULL THEN 0 ELSE 1 END AS num_non_null_fields
  FROM users_log

第 2 阶段:给定用户名的最大字段数是多少?

SELECT username, MAX(num_non_null_fields) AS num_non_null_fields
  FROM (SELECT username, sex, date_of_birth, zip,
               CASE WHEN sex           IS NULL THEN 0 ELSE 1 END +
               CASE WHEN date_of_birth IS NULL THEN 0 ELSE 1 END +
               CASE WHEN zip           IS NULL THEN 0 ELSE 1 END AS num_non_null_fields
          FROM users_log
       ) AS u
 GROUP BY username

第 3 阶段:为具有最大数量的非空字段的给定用户选择(全部)行:

SELECT u.username, u.sex, u.date_of_birth, u.zip
  FROM (SELECT username, MAX(num_non_null_fields) AS num_non_null_fields
          FROM (SELECT username, sex, date_of_birth, zip,
                       CASE WHEN sex           IS NULL THEN 0 ELSE 1 END +
                       CASE WHEN date_of_birth IS NULL THEN 0 ELSE 1 END +
                       CASE WHEN zip           IS NULL THEN 0 ELSE 1 END AS num_non_null_fields
                  FROM users_log
               ) AS u
         GROUP BY username
       ) AS v
  JOIN (SELECT username, sex, date_of_birth, zip,
               CASE WHEN sex           IS NULL THEN 0 ELSE 1 END +
               CASE WHEN date_of_birth IS NULL THEN 0 ELSE 1 END +
               CASE WHEN zip           IS NULL THEN 0 ELSE 1 END AS num_non_null_fields
          FROM users_log
       ) AS u
    ON u.username = v.username AND u.num_non_null_fields = v.num_non_null_fields;

现在,如果某人有多行(例如)填写了所有三个字段,则将返回所有这些行。但是,您没有指定在这些行之间进行选择的任何标准。

这里的基本技术可以适应任何变化的需求。关键是随时构建和测试子查询。

这些 SQL 都没有靠近 DBMS;里面可能有bug。

您尚未指定您使用的 DBMS。但是,Oracle 似乎不喜欢用于表别名的 AS 表示法,尽管它在列别名上使用 AS 没有问题。如果您使用的是任何其他 DBMS,则不必担心这种小怪癖。

【讨论】:

  • 哇,太棒了。 +1 :)
【解决方案2】:

幸运的是,您使用的是 PostgreSQL。通过将布尔值转换为整数来计算填充的字段更容易:

SELECT username, 
   ( 
      (sex is not null)::int 
    + (date_birth_birth is not null)::int 
    + (zip is not null)::int
   ) / 3.0 as percent_complete
FROM users_log

您的代码目标与此问题有相似之处:
Postgresql: Calculate rank by number of true OR clauses

【讨论】:

    猜你喜欢
    • 2011-02-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-04-26
    相关资源
    最近更新 更多