PL/SQL 结果不同于直接查询

Posted

技术标签:

【中文标题】PL/SQL 结果不同于直接查询【英文标题】:PL/SQL Result different than direct query 【发布时间】:2015-01-22 12:56:29 【问题描述】:

我创建了 PL/SQL 程序来更新学校注册排名。 看起来是这样的:

DECLARE
    studentsNumber NUMBER :=0;
    vSREDNIA RANK.SREDNIA%TYPE;
    vFIZYKA RANK.SREDNIA_FIZ%TYPE;
    vMATEMATYKA RANK.SREDNIA_MAT%TYPE;
    vPO RANK.SREDNIA_PO%TYPE;
    vWF RANK.SREDNIA_WF%TYPE;
    vREL RANK.SREDNIA_REL%TYPE;
    vMUZYKA RANK.SREDNIA_MUZ%TYPE;
BEGIN
    SELECT COUNT(*) INTO studentsNumber FROM uczniowie;
    FOR id IN 1..studentsNumber LOOP

        SELECT SUM(WAGA * WYNIK)/SUM(WAGA) INTO vSREDNIA FROM OCENY
        WHERE WLASCICIEL = id;

        SELECT SUM(WAGA * WYNIK)/SUM(WAGA) INTO vFIZYKA FROM OCENY
        WHERE WLASCICIEL = id AND PRZEDMIOT = 'Fizyka';

        SELECT SUM(WAGA * WYNIK)/SUM(WAGA) INTO vMATEMATYKA FROM OCENY
        WHERE WLASCICIEL = id AND PRZEDMIOT = 'Matematyka';

        SELECT SUM(WAGA * WYNIK)/SUM(WAGA) INTO vPO FROM OCENY
        WHERE WLASCICIEL = id AND PRZEDMIOT = 'Przysposobienie Obronne';

        SELECT SUM(WAGA * WYNIK)/SUM(WAGA) INTO vWF FROM OCENY
        WHERE WLASCICIEL = id AND PRZEDMIOT = 'WF';

        SELECT SUM(WAGA * WYNIK)/SUM(WAGA) INTO vREL FROM OCENY
        WHERE WLASCICIEL = id AND PRZEDMIOT = 'Religioznastwo';

        SELECT SUM(WAGA * WYNIK)/SUM(WAGA) INTO vMUZYKA FROM OCENY
        WHERE WLASCICIEL = id AND PRZEDMIOT = 'Muzyka';

        DBMS_OUTPUT.PUT_LINE('ID:' || id || ' Srednia: '|| vSREDNIA);

        UPDATE RANK
        SET SREDNIA = vSREDNIA, SREDNIA_MAT = vMATEMATYKA, SREDNIA_FIZ = vFIZYKA,
         SREDNIA_REL = vREL, SREDNIA_MUZ = vMUZYKA, SREDNIA_PO = vPO, SREDNIA_WF = vWF
        WHERE ID_UCZNIA = id;

    END LOOP;
    COMMIT;
END;

但 SREDNIA 和 SREDNIA_WF 列始终为 4,其他列为空。例如,当我输入控制台时:

        SELECT SUM(WAGA * WYNIK)/SUM(WAGA) FROM OCENY
        WHERE WLASCICIEL = 3;

我得到了适当的结果:3,73913043。 这种差异从何而来?如果有意义的话,它就是 Oracle 数据库。 DBMS_OUTPUT 产生 1,2,3...ID,所以没关系。

【问题讨论】:

【参考方案1】:

例如看

SQL> create table tmp$i (i number(38, 0), j number(38, 2), k number(38, 4));

Table created

SQL> insert into tmp$i values (2.55, 2.55, 2.55);

1 row inserted

SQL> select * from tmp$i;

         I          J          K
---------- ---------- ----------
         3       2,55     2,5500

检查您的表格字段是否以适当的比例描述。

【讨论】:

【参考方案2】:

除了检查排名表列的数字精度是否正确之外,您的代码也不可靠。

您不仅在进行逐行(也称为逐行)处理,而且您可能无法获得所有正确的学生 ID。 “计算学生人数,然后假设所有 ids 从 1 开始并且是连续的”你做的循环步骤实际上可能不正确。如果最小的学生 id 是 2 怎么办?如果学生编号有空缺怎么办?你最终会为一些 id 做不必要的工作,而完全错过其他的。至少,您应该将其转换为循环游标,以确保您选择了 uczniowie 表中的所有 id。

此外,所有这些单独的选择都可以在一次选择中完成。当您使用它时,您可以将其合并到单个更新/合并语句中。因此首先否定了所有循环的需要,例如(未经测试,因为您没有提供任何示例数据):

merge into rank tgt
using (select wlasciciel student_id,
              sum(waga * wynik)/sum(waga) vsrednia,
              sum(case when przedmiot = 'Fizyka' then waga * wynik end)/sum(case when przedmiot = 'Fizyka' then waga end) vfizyka,
              sum(case when przedmiot = 'Matematyka' then waga * wynik end)/sum(case when przedmiot = 'Matematyka' then waga end) vmatematyka,
              sum(case when przedmiot = 'Przysposobienie Obronne' then waga * wynik end)/sum(case when przedmiot = 'Przysposobienie Obronne' then waga end) vpo,
              sum(case when przedmiot = 'WF' then waga * wynik end)/sum(case when przedmiot = 'WF' then waga end) vwf,
              sum(case when przedmiot = 'Religioznastwo' then waga * wynik end)/sum(case when przedmiot = 'Religioznastwo' then waga end) vrel,
              sum(case when przedmiot = 'Muzyka' then waga * wynik end)/sum(case when przedmiot = 'Muzyka' then waga end) vmuzyka
       from   oceny
       group by wlasciciel) src
  on (tgt.id_ucznia = src.student_id)
when matched then
  update set tgt.srednia = src.vsrednia,
             tgt.srednia_mat = src.vmatematyka,
             tgt.srednia_fiz = src.vfizyka,
             tgt.srednia_rel = src.vrel,
             tgt.srednia_muz = src.vmuzyka,
             tgt.srednia_po = src.vpo,
             tgt.srednia_wf = src.vwf
when not matched then -- optional insert clause, just in case the id isn't already present in the rank table
  insert (tgt.id_ucznia,
          tgt.srednia,
          tgt.srednia_mat,
          tgt.srednia_fiz,
          tgt.srednia_rel,
          tgt.srednia_muz,
          tgt.srednia_po,
          tgt.srednia_wf)
  values (src.student_id,
          src.vsrednia,
          src.vmatematyka,
          src.vfizyka,
          src.vrel,
          src.vmuzyka,
          src.vpo,
          src.vwf);

您会发现单个语句将优于您的过程(1. 您不必为每个 id 查询同一个表 7 次,2. 您不必在 pl/sql 和 sql 之间切换上下文每次移动到另一个 ID 时)。它也应该更容易调试,因为现在您可以单独运行单个 select 语句来查看输出并检查它是否与您期望的匹配。

【讨论】:

以上是关于PL/SQL 结果不同于直接查询的主要内容,如果未能解决你的问题,请参考以下文章

PL/SQL ORACLE 查询作为结果

Oracle PL/SQL查询结果如何自动换行

PL/SQL 和 SQL Developer 的结果不同

无法从 cmd PL/SQL 中的简单查询中获得正确的结果

ORA-06504: PL/SQL: 执行时返回结果集变量的类型

PL/SQL查询,字段名添加中文别名,查询结果的字段名会显示问号,处理方法: