【问题标题】:SQL One to Many with possible UNIONSQL 一对多与可能的 UNION
【发布时间】:2016-09-15 20:21:56
【问题描述】:

我对正在构建的查询有疑问,我想在 SQL 语句中完成所有操作。

所以,我有一张这样的数据表:

+-----+-----+-----+-----+
| pid | did | src | val |
+-----+-----+-----+-----+
|   1 |   1 | lab |   0 |
|   1 |   1 | lab |   1 |
|   2 |   3 | pl  |   1 |
|   1 |   1 | pl  |   1 |
|   2 |   1 | med |   1 |
|   1 |   1 | med |   1 |
+-----+-----+-----+-----+

有一个 ID 列 (pid)、一个疾病列 (did)、一个源 (src) 列和一个值 (val) 列。疾病列还有一个我这样创建的标准化表:

+-----+-----------------+
| did |      name       |
+-----+-----------------+
|   1 | Lung Cancer     |
|   2 | Alcoholism      |
|   3 | Obesity         |
|   4 | Diabetes        |
|   5 | Viral Infection |
+-----+-----------------+ 

这里真正的诀窍是我需要执行一个查询,该查询会生成一个表,该表会生成一个新表,该表显示每个 pid (ID),以及疾病 (did) 的相关文本名称,这可能是一个 JOIN使用规范化表。

棘手的部分是,对于每个 ID,我需要计算 (3) 个可能的来源 (src) 中每个的出现次数,因此可能会出现 5-10 个 ID 为 1,并且每个这些将具有关联的 src:MED、LAB 或 PL。我需要对每条记录进行计数并显示在如下表中。

+-----+------------------------+--------------------------+----------------------+
| pid |      Lung Cancer       |        Alcoholism        |       Obesity              
+-----+------------------------+--------------------------+----------------------+
|   1 | Yes                    | Maybe                    | No                   |
|     | (med: 2, lab: 1, pl:1) | (med: 0, lab: 1, pl:1)   | (med:0, lab:0, pl:0) |
+-----+------------------------+--------------------------+----------------------+

因此,可能有 5-10 行 ID 为 1,并且对于每一行,将分配一个 src(源),它将是三个值之一(lab、pl、med),它们将有一个值 0 零或 1 1。

最后会有一个计数,基本上达到三个状态(是)他们有疾病,(每个来源都为 1 或以上),或两个为 0,但一个有 1 或更多(也许),或全零,(否)

我不确定所有这些都可以通过 SQL 语句完成......或者我可以使用 JOIN/UNION 或某种组合来构建一个新表并放置所有值,然后对其进行 json_encode(我正在使用PHP),然后使用 Angular 对 src 值进行排序并进行计算并显示“是”、“否”或“可能”的引导按钮

感谢大家的帮助!这是一个相当令人费解的 SQL 语句,至少我是这么认为的。

【问题讨论】:

  • 哪个 RDBMS? mysql? sql服务器?甲骨文?你试过什么?您似乎更可能使用条件聚合来寻找枢轴情况。
  • 为了让这一切都在 SQL 中工作,您可能需要为每个不同类型的 src 建立一个相关的子查询来获取它们的总数。这通常是将一行或多行放入结果中的多列的唯一方法。使用分组和求和将两个表连接起来然后将行转换为 php 中的列会更容易。
  • 您可以通过 GROUP BY 实现大部分目标。数据的实际呈现可以使用 PHP 或 Angular 中的少量程序代码来处理,您可以使用对医疗数据表有效的 HTML 表。您将选择 *, SUM(val) as val FROM pidTable JOIN didTABLE ON didTable.did = pidTable.did GROUP BY pid, didTable.did, src
  • @gview -- 这毫无意义 HTML 与这个问题有什么关系?
  • @Hogan,我建议使用 HTML 呈现最终输出,而不是尝试在 SQL 中创建伪表。应该在 PHP 或 Angular 中完成演示文稿/html 输出。用户在问题中提到了这两种方法的使用——我没有建议它们。

标签: php sql angularjs json


【解决方案1】:

这很简单,只需逐步完成..首先将 src 卷起来:

SELECT pid, did, 
       sum(CASE WHEN src='med' THEN val ELSE O END) AS med,
       sum(CASE WHEN src='lab' THEN val ELSE O END) AS lab,
       sum(CASE WHEN src='pl' THEN val ELSE O END) AS pl
FROM table_of_data
GROUP BY pid, did

现在按 did 拉入列

SELECT pid,
       max(CASE WHEN did=1 THEN '(med:'|| CAST(med as varchar(5)) ||
                            ', lab:'|| CASE(lab as varchar(5)) ||
                            ', pl:' || CAST(pl as varhcar(5)) || ')' 
                       ELSE null END) AS Lung_Cancer,  
       max(CASE WHEN did=2 THEN '(med:'|| CAST(med as varchar(5)) ||
                            ', lab:'|| CASE(lab as varchar(5)) ||
                            ', pl:' || CAST(pl as varhcar(5)) || ')' 
                       ELSE null END) AS Alcoholism,  
       max(CASE WHEN did=3 THEN '(med:'|| CAST(med as varchar(5)) ||
                            ', lab:'|| CASE(lab as varchar(5)) ||
                            ', pl:' || CAST(pl as varhcar(5)) || ')' 
                       ELSE null END) AS Obesity,  
       max(CASE WHEN did=4 THEN '(med:'|| CAST(med as varchar(5)) ||
                            ', lab:'|| CASE(lab as varchar(5)) ||
                            ', pl:' || CAST(pl as varhcar(5)) || ')' 
                       ELSE null END) AS Diabetes,  
       max(CASE WHEN did=5 THEN '(med:'|| CAST(med as varchar(5)) ||
                            ', lab:'|| CASE(lab as varchar(5)) ||
                            ', pl:' || CAST(pl as varhcar(5)) || ')' 
                       ELSE null END) AS Viral_Infection,  
FROM prior_result
GROUP BY pid

要获得是否,可以使用先前结果中的案例

SELECT pid, did,
       CASE WHEN med+lab+pl > 3 THEN 'Yes'
            WHEN med+lab+pl = 0 THEN 'No'
            ELSE 'Maybe' END AND as have_it
FROM prior_result

没问题......把它们放在一起,你就得到了

SELECT pid,
       max(CASE WHEN did=1 THEN '(med:'|| CAST(med as varchar(5)) ||
                            ', lab:'|| CASE(lab as varchar(5)) ||
                            ', pl:' || CAST(pl as varhcar(5)) || ')' 
                       ELSE null END) AS Lung_Cancer,  
       max(CASE WHEN did=1 THEN
                             CASE WHEN med+lab+pl > 3 THEN 'Yes'
                                  WHEN med+lab+pl = 0 THEN 'No'
                                  ELSE 'Maybe'
                              END
                            ELSE null END) AS Have_Lung_Cancer,  

       max(CASE WHEN did=2 THEN '(med:'|| CAST(med as varchar(5)) ||
                            ', lab:'|| CASE(lab as varchar(5)) ||
                            ', pl:' || CAST(pl as varhcar(5)) || ')' 
                       ELSE null END) AS Alcoholism,  
       max(CASE WHEN did=2 THEN
                             CASE WHEN med+lab+pl > 3 THEN 'Yes'
                                  WHEN med+lab+pl = 0 THEN 'No'
                                  ELSE 'Maybe'
                              END
                            ELSE null END) AS Have_Alcoholism,  

       max(CASE WHEN did=3 THEN '(med:'|| CAST(med as varchar(5)) ||
                            ', lab:'|| CASE(lab as varchar(5)) ||
                            ', pl:' || CAST(pl as varhcar(5)) || ')' 
                       ELSE null END) AS Obesity,  
       max(CASE WHEN did=3 THEN
                             CASE WHEN med+lab+pl > 3 THEN 'Yes'
                                  WHEN med+lab+pl = 0 THEN 'No'
                                  ELSE 'Maybe'
                              END
                            ELSE null END) AS Have_Obesity,  

       max(CASE WHEN did=4 THEN '(med:'|| CAST(med as varchar(5)) ||
                            ', lab:'|| CASE(lab as varchar(5)) ||
                            ', pl:' || CAST(pl as varhcar(5)) || ')' 
                       ELSE null END) AS Diabetes,  
       max(CASE WHEN did=4 THEN
                             CASE WHEN med+lab+pl > 3 THEN 'Yes'
                                  WHEN med+lab+pl = 0 THEN 'No'
                                  ELSE 'Maybe'
                              END
                            ELSE null END) AS Have_Diabetes,  

       max(CASE WHEN did=5 THEN '(med:'|| CAST(med as varchar(5)) ||
                            ', lab:'|| CASE(lab as varchar(5)) ||
                            ', pl:' || CAST(pl as varhcar(5)) || ')' 
                       ELSE null END) AS Viral_Infection,  
       max(CASE WHEN did=5 THEN
                             CASE WHEN med+lab+pl > 3 THEN 'Yes'
                                  WHEN med+lab+pl = 0 THEN 'No'
                                  ELSE 'Maybe'
                              END
                            ELSE null END) AS Have_Viral_Infection,  

FROM (
  SELECT pid, did, 
       sum(CASE WHEN src='med' THEN val ELSE O END) AS med,
       sum(CASE WHEN src='lab' THEN val ELSE O END) AS lab,
       sum(CASE WHEN src='pl' THEN val ELSE O END) AS pl
  FROM table_of_data
  GROUP BY pid, did
) sub
GROUP BY pid

【讨论】:

  • 每次添加新的疾病或来源时,都需要更改 SQL 代码。不是很相关,但确实表明如果您想使用大量 SQL 函数,您可以做任何事情。
  • 你们真是天才。这是一个很好的开始。谢谢你为我设计了这个。我会尝试一下,也许稍后再回来查看。其他有想法的人也发帖。我从你们那里学到了很多东西。
  • @gview 它也与此非常相关....是第一步。
  • 是的,马特我不知道你会怎么做是,不,也许评估。也许用 angular 迭代前端的 json 并显示红色、黄色、绿色按钮或类似按钮的引导按钮类......但同样,优雅地,如果你这样做将有数千万条记录.. ..也许不是一个好主意。
  • @matt -- 我认为这只是一个案例陈述......我必须重新阅读这些要求
【解决方案2】:

看到您可能需要在某个时候动态地计算出所有这些,这是一种获得结果的方法,然后以比@hogan 的答案少一点的输入来进行调整,但这是基于他的答案。

SELECT
    d.pid
    ,COALESCE(MAX(CASE WHEN d.did = 1 THEN colString END),'No (med:0 lab:0 pl:0)') as Lung_Cancer
    ,COALESCE(MAX(CASE WHEN d.did = 2 THEN colString END),'No (med:0 lab:0 pl:0)') as Alcoholism
    ,COALESCE(MAX(CASE WHEN d.did = 3 THEN colString END),'No (med:0 lab:0 pl:0)') as Obesity
FROM
    (
     SELECT pid, did, 
          CONCAT(CASE
             WHEN SUM(Val) > 2 THEN 'Yes'
             WHEN SUM(Val) > 1 THEN 'Maybe'
             ELSE 'No'
          End
          ,' (med:'
          ,CAST(SUM(CASE WHEN src='med' THEN val ELSE 0 END) AS CHAR(5))
          ,' lab:'
          ,CAST(SUM(CASE WHEN src='lab' THEN val ELSE 0 END) AS CHAR(5))
          ,' pl:'
          ,CAST(SUM(CASE WHEN src='pl' THEN val ELSE 0 END) AS CHAR(5))
          ,')') as colString

     FROM TableP
     GROUP BY pid, did
    ) d
GROUP BY
    d.pid

请注意,当没有特定疾病的 PID 值时,使用 COALESCE 处理。它的 sql 小提琴:http://www.sqlfiddle.com/#!9/44ac9/11

只是为了向您展示这一切都可以在没有子查询的情况下完成:

SELECT
    pid
    ,CONCAT(CASE
       WHEN SUM(CASE WHEN did = 1 THEN val ELSE 0 END) > 2 THEN 'Yes'
       WHEN SUM(CASE WHEN did = 1 THEN val ELSE 0 END) > 1 THEN 'Maybe'
       ELSE 'No'
    END
    , ' (med:'
    ,CAST(SUM(CASE WHEN did = 1 and src = 'med' THEN val ELSE 0 END) AS CHAR(5))
    , ', lab:'
    ,CAST(SUM(CASE WHEN did = 1 and src = 'lab' THEN val ELSE 0 END) AS CHAR(5))
    , ', pl:'
    ,CAST(SUM(CASE WHEN did = 1 and src = 'pl' THEN val ELSE 0 END) AS CHAR(5))
    ,')' ) AS Lung_Cancer

    .......repeat per disease

FROM
    table_of_data
GROUP BY
    pid

这种技术的 SQL 小提琴http://www.sqlfiddle.com/#!9/44ac9/13

【讨论】:

  • 感谢大家的利他主义和提出建议的意愿。它为我的项目提供了一些很棒的想法!
【解决方案3】:

由于您还有一定数量的有效来源,因此您也应该为它们准备一个表格:

+-----+-----+ |源代码 |位置 | +-----+-----+ |实验室 | 2 | |医学 | 1 | | pl | 3 | +-----+-----+

pos 列表示您希望在结果中的哪个位置显示来源。)

然后交叉连接疾病和来源以获得所有组合。外部连接数据表以获得所需的 pid 和每个疾病和源的计数。剩下的主要是字符串拼接:

set @pid = 1;

select
  pid, 
  name, 
  concat
  (
    case when count(sumval) <> count(*) then 'Maybe'
         when min(sumval) > 0 then 'Yes' 
         when max(sumval) = 0 then 'No'
         else 'Maybe'
    end,
    ' (',
    group_concat(concat(src, ': ', coalesce(sumval, '?')) order by pos separator ', '),
    ')'
  ) as result
from
(
  select @pid as pid, d.*, s.*, sum(data.val) as sumval 
  from desease d
  cross join source s
  left join data on  data.did = d.did
                 and data.src = s.src 
                 and data.pid = @pid
  group by d.did, s.src
) sumvals
group by did
having count(sumval) > 0;

这会为您提供如下行:

+-----+-----------------+------------- ------+ |进程号 |姓名 |结果 | +-----+-----------------+------------- ------+ | 1 |肺癌 |是(医学:2,实验室:1,pl:1)| | 1 |酗酒 |也许(医学:0,实验室:1,pl:1)| | 1 |肥胖 |否(医学:0,实验室:0,pl:0)| +-----+-----------------+------------- ------+

当没有 pid、desease 和 source 条目时,我们不知道该 deease 是否适用。在这种情况下,sumval 为空。如果甚至没有一个疾病条目,那么我们根本不显示它(参见having 子句)。否则,如果至少给出了一个来源而缺少另一个来源,我们会得出“可能”的结论。

这里是 SQL 小提琴:http://sqlfiddle.com/#!9/c2ac29/1

您的 GUI 层的任务是关心布局(在您的情况下:在列中显示结果)。

【讨论】:

  • @Matt:你是对的。我将不存在的条目视为零值存在。这当然是错误的,甚至无法在 GUI 中彻底处理(因为那时我们不知道零是指零还是没有数据)。我已经更正了我的答案,因此当没有 pid 的数据时,现在不会显示疾病,而当疾病的来源不完整时,会显示“可能”。
猜你喜欢
  • 2022-01-15
  • 2015-03-22
  • 1970-01-01
  • 2014-02-10
  • 1970-01-01
  • 1970-01-01
  • 2014-09-26
  • 1970-01-01
相关资源
最近更新 更多