【问题标题】:Oracle SQL - multiple JOINS performance IssueOracle SQL - 多个 JOINS 性能问题
【发布时间】:2021-01-28 16:51:27
【问题描述】:

我目前正在尝试为 Oracle DB 编写查询,以获取有关患者住院时间的一些信息。对于他每天在特定领域获得特定分数 - 因此我必须加入多个表格才能获得实际的每日分数(而不是完整分数)。

我的第一次尝试(这是有效的,但我想以另一种格式显示)是这样的:

SELECT
    f.FALLID AS "ID",
    f.FALLNR AS "Fallnummer",
    DECODE(e.SCOREID, 12, 'SAPS', 13, 'TISS', 'X') AS "Score",
    to_char(e.VON, 'DD.MM.YYYY') AS "Datum (von)",
    -- to_char(e.BIS, 'DD.MM.YYYY') AS "Datum (bis)",
    scp.BEZEICHNUNG AS "Bezeichnung",
    s.PUNKTE AS "Punkte"
FROM
    FALL f
    JOIN OS_MUP.EINSTUFUNG e ON e.FALLID = f.FALLID
    JOIN OS_MUP.EINSTUFUNGSRANG einr ON einr.EINSTUFUNGID = e.EINSTUFUNGID
    JOIN OS_MUP.SCORERANG s ON s.SCORERANGID = einr.SCORERANGID
    JOIN OS_MUP.SCOREPOSITION scp ON scp.SCOREPOSITIONID = s.SCOREPOSITIONID
WHERE
    f.FALLNR = 1234567
ORDER BY
    e.SCOREID, e.VON

这以以下形式显示结果:

+------+---------+-------+------------+-------------+--------+
|  ID  | Fallnr  | Score |    Date    | Description | Points |
+------+---------+-------+------------+-------------+--------+
| 1234 | 1234567 | ABCD  | 05.05.2020 | Age         |     12 |
| 1234 | 1234567 | ABCD  | 05.05.2020 | Temperature |      2 |
+------+---------+-------+------------+-------------+--------+

没关系,但我不想重复列的日期和每天的描述,我想得到这样的外观:

+------+---------+-------+------------+-----+-------------+
|  ID  | Fallnr  | Score |    Date    | Age | Temperature |
+------+---------+-------+------------+-----+-------------+
| 1234 | 1234567 | ABCD  | 05.05.2020 |  12 |           2 |
| 1234 | 1234567 | ABCD  | 06.05.2020 |  12 |           2 |
+------+---------+-------+------------+-----+-------------+

所以我修改了查询并在此刻完成了这个:

variable var number
exec :var := 2068237610

SELECT
    dt1.FALLNR AS "Fallnummer",
    to_char(dt1.VON, 'DD.MM.YYYY') AS "Datum",
    dt1.PUNKTE AS "Alter",
    dt2.PUNKTE AS "Herzfrequenz"
    --dt3.PUNKTE AS "RR Syst.",
    --dt4.PUNKTE AS "Körpertemp.",
    --dt5.PUNKTE AS "PaO2/FiO2"
    -- dt6.PUNKTE AS "Urin",
    -- dt7.PUNKTE AS "Leukozyten"
    FROM (
    SELECT
        f.FALLID,
        f.FALLNR,
        s.PUNKTE,
        e.VON
    FROM Fall f
        JOIN OS_MUP.EINSTUFUNG e ON e.FALLID = f.FALLID
        JOIN OS_MUP.EINSTUFUNGSRANG einr ON einr.EINSTUFUNGID = e.EINSTUFUNGID
        JOIN OS_MUP.SCORERANG s ON s.SCORERANGID = einr.SCORERANGID
    WHERE
        s.SCOREPOSITIONID = 10003 AND f.FALLNR = :var /* Alter */
    ) dt1
JOIN
    (SELECT
        f.FALLNR,
        s.PUNKTE,
        e.VON
    FROM Fall f
        JOIN OS_MUP.EINSTUFUNG e ON e.FALLID = f.FALLID
        JOIN OS_MUP.EINSTUFUNGSRANG einr ON einr.EINSTUFUNGID = e.EINSTUFUNGID
        JOIN OS_MUP.SCORERANG s ON s.SCORERANGID = einr.SCORERANGID
    WHERE
        s.SCOREPOSITIONID = 10004 AND f.FALLNR = :var /* Herzfrequenz */
    ) dt2
ON dt1.FALLNR = dt2.FALLNR AND dt1.VON = dt2.VON
ORDER BY dt1.VON

这是我希望它工作的工作,但我必须一遍又一遍地进行这些连接以获得更多列,并且还有 5-6 更多列 - 以及由创建的嵌套循环的原因加入这个版本的查询非常慢(比如慢了 250 秒)。

是否有机会以更少使用联接的另一种方式实现我的目标?

提前致谢!

【问题讨论】:

    标签: sql oracle performance


    【解决方案1】:

    如何进行转置

    一个选项是枢轴:https://www.oracletutorial.com/oracle-basics/oracle-pivot/ 或更多选项:https://livesql.oracle.com/apex/livesql/file/tutorial_GNZ3LQPJ0K6RTD1NEEPNRQT0R.html

    主要缺点:

    • 直接 SQL 的固定项目列表
    • 仅动态项目的 XML 输出

    然后是老式的方式:

    • 先准备好数据
    • 折叠列以输出和分配

    类似的东西:

    WITH
      cte_position
    (
      key_position,
      name_position
    )
    AS
    ( -- manual preparation of column name list
      SELECT Cast( 10003 AS number(6,0) ) AS key_position, Cast( 'Alter' AS varchar2(20 char) ) AS name_position FROM dual
      UNION ALL
      SELECT Cast( 10004 AS number(6,0) ) AS key_position, Cast( 'Herzfrequenz' AS varchar2(20 char) ) AS name_position FROM dual
    ),
      cte_data
    (
      fallnr,
      punkte,
      column_group,
      von
    )
    AS
    ( -- combine data and prepare for single pass data collection
      SELECT
        f.FALLNR,
        e.VON,
        punkt_name.name_position AS column_group,
        s.PUNKTE
      FROM Fall f
        INNER JOIN OS_MUP.EINSTUFUNG e 
          ON  e.FALLID = f.FALLID
        INNER JOIN OS_MUP.EINSTUFUNGSRANG einr 
          ON  einr.EINSTUFUNGID = e.EINSTUFUNGID
        INNER JOIN OS_MUP.SCORERANG s 
          ON  s.SCORERANGID = einr.SCORERANGID
        -- link (and filter) by required positions to show
        INNER JOIN cte_position punkt_name
          ON  s.SCOREPOSITIONID = punkt_name.key_position
      WHERE -- specific patient
          f.FALLNR = :var 
    )
    SELECT
      patient.fallnr AS Fallnummer,
      To_char( patient.von, 'DD.MM.YYYY' ) AS Datum, -- non ISO 8601 date
      Max( CASE WHEN patient.column_group = 'Alter' THEN patient.punkte ELSE 0 END ) AS "Punkt Alter",
      Max( CASE WHEN patient.column_group = 'Herzfrequenz' THEN patient.punkte ELSE 0 END ) AS "Punkt Herzfrequenz"
    FROM
      cte_data patient
    GROUP BY
      patient.fallnr,
      patient.von
    ;
    

    同样,这种方法也有一些缺点:

    • 每个 Max 函数都必须单独命名

    另一方面,这是一次捕获数据的过程,理论上可以加快数据收集速度。如果不是,则患者代码上缺少索引。

    【讨论】:

      【解决方案2】:

      获取您的原始查询并在其上使用PIVOT

      SELECT *
      FROM   (
        SELECT f.FALLID AS ID,
               f.FALLNR AS Fallnummer,
               DECODE(e.SCOREID, 12, 'SAPS', 13, 'TISS', 'X') AS Score,
               TO_CHAR(e.VON, 'DD.MM.YYYY') AS Datum,
               scp.BEZEICHNUNG,
               s.PUNKTE
        FROM   FALL f
               INNER JOIN OS_MUP.EINSTUFUNG e
               ON ( e.FALLID = f.FALLID )
               INNER JOIN OS_MUP.EINSTUFUNGSRANG einr
               ON ( einr.EINSTUFUNGID = e.EINSTUFUNGID )
               INNER JOIN OS_MUP.SCORERANG s
               ON ( s.SCORERANGID = einr.SCORERANGID )
               JOIN OS_MUP.SCOREPOSITION scp
               ON ( scp.SCOREPOSITIONID = s.SCOREPOSITIONID )
        WHERE  f.FALLNR = 1234567
      )
      PIVOT (
        MAX( Punkte ) FOR BEZEICHNUNG IN (
          'Age' AS Age,
          'Temperature' AS Temperature,
          'Alter' AS Alter,
          'Herzfrequenz' AS Herzfrequenz,
          'RR Syst.' AS "RR Syst.",
          'Körpertemp' AS "Körpertemp.",
          'PaO2/FiO2' AS "PaO2/FiO2"
          -- ...
        )
      )
      ORDER BY
             SCOREID,
             VON
      

      【讨论】:

      • 这正是我所需要的!我不知道 PIVOT 存在,因为我是 Oracle 的新手。非常感谢!
      猜你喜欢
      • 2018-01-29
      • 1970-01-01
      • 1970-01-01
      • 2016-09-09
      • 2013-11-08
      • 2012-03-04
      • 2010-10-08
      • 2011-05-03
      • 1970-01-01
      相关资源
      最近更新 更多