Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/sql/79.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
当缺少行时,在Oracle SQL PIVOT结果中用自定义值替换空值_Sql_Oracle_Pivot - Fatal编程技术网

当缺少行时,在Oracle SQL PIVOT结果中用自定义值替换空值

当缺少行时,在Oracle SQL PIVOT结果中用自定义值替换空值,sql,oracle,pivot,Sql,Oracle,Pivot,当没有要透视的行时,透视查询重新运行NULL 在以下示例中,id=2缺少颜色属性 将src_数据id、attr_名称、attr_id、attr_类型设置为 从DUAL UNION ALL中选择1,‘意大利’、‘意大利’、‘国家’- 从DUAL UNION ALL中选择1、‘绿色’、‘G’、‘颜色’- 从DUAL UNION ALL中选择1、‘大’、‘B’、‘大小’- 从DUAL UNION ALL中选择2,‘法国’、‘法国’、‘国家’- 从“双”菜单中选择2、‘小’、‘S’、‘大小’- 从src

当没有要透视的行时,透视查询重新运行NULL

在以下示例中,id=2缺少颜色属性

将src_数据id、attr_名称、attr_id、attr_类型设置为 从DUAL UNION ALL中选择1,‘意大利’、‘意大利’、‘国家’- 从DUAL UNION ALL中选择1、‘绿色’、‘G’、‘颜色’- 从DUAL UNION ALL中选择1、‘大’、‘B’、‘大小’- 从DUAL UNION ALL中选择2,‘法国’、‘法国’、‘国家’- 从“双”菜单中选择2、‘小’、‘S’、‘大小’- 从src_数据中选择* PIVOT MAXATTR\u名称作为名称,MAXATTR\u ID作为ID- 对于属性类型,将“国家”作为国家,“颜色”作为颜色,“大小”作为大小; 结果是

身份证件 国名 国家识别号 颜色名称 颜色标识 大小/名称 尺码 1. 意大利 信息技术 绿色 G 大的 B 2. 法国 FR 无效的 无效的 小的 s
我将使用聚合函数和CASE来完成透视

with src_data (id, attr_name, attr_id, attr_type) as (
    select 1, 'ITALY', 'IT', 'COUNTRY' FROM DUAL UNION ALL --
    select 1, 'GREEN', 'G', 'COLOR' FROM DUAL UNION ALL --
    select 1, 'BIG', 'B', 'SIZE' FROM DUAL UNION ALL --
    select 2, 'FRANCE', 'FR', 'COUNTRY' FROM DUAL UNION ALL --
    select 2, 'SMALL', 'S', 'SIZE' FROM DUAL  --
)
SELECT ID,
       NVL(MAX(CASE WHEN attr_type = 'COUNTRY' THEN attr_name  END),'N/A') "COUNTRY_NAME",
       NVL(MAX(CASE WHEN attr_type = 'COUNTRY' THEN attr_id END),'N/A') "COUNTRY_ID",
       NVL(MAX(CASE WHEN attr_type = 'COLOR' THEN attr_name END),'N/A') "COLOR_NAME",
       NVL(MAX(CASE WHEN attr_type = 'COLOR' THEN attr_id END),'N/A') "COLOR_ID",
       NVL(MAX(CASE WHEN attr_type = 'SIZE' THEN attr_name END),'N/A') "SIZE_NAME",
       NVL(MAX(CASE WHEN attr_type = 'SIZE' THEN attr_id END),'N/A') "SIZE_ID"
FROM src_data
GROUP BY ID

如果您想要更具弹性的结构,而不是手动写入多个NVL,则更喜欢创建一个存储函数,该函数将返回SYS\u REFCURSOR作为数据类型,例如

CREATE OR REPLACE FUNCTION Pivot_Src_Data RETURN SYS_REFCURSOR IS
  v_recordset SYS_REFCURSOR;
  v_sql       VARCHAR2(32767);
  v_cols1     VARCHAR2(32767);
  v_cols2     VARCHAR2(32767);  
BEGIN
  SELECT LISTAGG( 'NVL('||attr_type||'_'||typ||',''N/D'') AS "'||attr_type||'_'||typ||'"' , ',' )
          WITHIN GROUP ( ORDER BY attr_type ) 
    INTO v_cols1
    FROM ( SELECT DISTINCT attr_type, typ 
             FROM src_data
            CROSS JOIN (SELECT 'ID' AS typ FROM dual UNION ALL SELECT 'NAME' FROM dual) );

  SELECT LISTAGG( ''''||attr_type||''' AS "'||attr_type||'"' , ',' )
          WITHIN GROUP ( ORDER BY attr_type )
    INTO v_cols2
    FROM ( SELECT DISTINCT attr_type FROM src_data );
                
  v_sql := 'SELECT NVL(ID,-1) AS ID, '|| v_cols1 ||
           '  FROM src_data s
             PIVOT (MAX(attr_name) AS name, MAX(attr_id) AS id 
               FOR attr_type IN ('|| v_cols2 ||'))';

  OPEN v_recordset FOR v_sql;
  RETURN v_recordset;
END;
/
它将作为

SQL> DECLARE
    res SYS_REFCURSOR;
BEGIN
   :res := Pivot_Src_Data;
END;
/

SQL> PRINT res;
这样,无论向表中添加多少新属性类型,都会在数据透视结果集中看到所有属性类型,除非作为LISTAGG函数参数显示的字符串长度超过该函数的阈值4000。

可以通过以下方法执行此操作:

如果您有一个包含所有属性类型的表,那么生成所有属性类型的列表效果最好。您可以从源数据生成一个不同的列表,但在大型数据集上这可能会很慢 外部使用on id将数据连接到此。这将为上面列表中每个id的每个属性提供一行 在子查询中将空名称/ID转换为N/A,-1(视情况而定) 将输出传递给pivot 其中:

with src_data (id, attr_name, attr_id, attr_type) as (
  select 1, 'ITALY', 'IT', 'COUNTRY' FROM DUAL UNION ALL --
  select 1, 'GREEN', 'G', 'COLOR' FROM DUAL UNION ALL --
  select 1, 'BIG', 'B', 'SIZE' FROM DUAL UNION ALL --
  select 2, 'FRANCE', 'FR', 'COUNTRY' FROM DUAL UNION ALL --
  select 2, 'SMALL', 'S', 'SIZE' FROM DUAL  --
), attrs as (
  select distinct attr_type from src_data
), id_attrs as (
  select id, attr_type,
         nvl ( attr_name, 'N/A' ) attr_name,
         nvl ( attr_id, -1 ) attr_id
  from   attrs a
  left   join src_data d
    partition by ( id ) 
  using ( attr_type ) 
)
select * from id_attrs
pivot (
  max(attr_name) as name, max(attr_id) as id --
  for attr_type in (
    'COUNTRY' AS "COUNTRY", 'COLOR' AS "COLOR", 'SIZE' AS "SIZE"
  )
);

ID    COUNTRY_NAME   COUNTRY_ID   COLOR_NAME   COLOR_ID   SIZE_NAME   SIZE_ID   
    1 ITALY          IT           GREEN        G          BIG         B          
    2 FRANCE         FR           N/A          -1         SMALL       S    
MAXNVL语法实际上似乎提供了正确的结果,即使对于没有相应行要透视的列,也会替换空值


我认为这种情况违反了在PIVOT生成的所有列上添加特定NVL逻辑的规则。我的真实案例有14个专栏,OP提到了这些。很有趣。因此,您基本上用更重但更灵活的语法重新实现了pivot。本机pivot是否有任何可能的解决方案?@user103716。我认为这种语法比pivot更简单,而且更灵活。pivot除了不是SQL标准的一部分之外,对我来说一直都很神秘。@user103716-这里的响应者没有重新实现pivot。根据定义,数据透视是条件聚合,这个答案说明了Oracle在11.1版中引入pivot运算符之前是如何进行数据透视的,以及在任何其他数据库方言中是如何进行数据透视的。在外部SELECT子句中使用NVL有什么错?无论如何,选择*通常是一种糟糕的做法。显式命名列,并根据需要使用NVL。@mathguy,这很难看,因为您必须对pivot返回的所有列重复特定于NVL的逻辑。如果您添加了一个新的属性类型,您需要记住在最终选择中再次添加它。抱歉,但是您的理由没有意义。您说过要对name列使用N/D,对id列使用-1,因此占位符依赖于列。如果您添加了一个新的属性类型,不管这意味着什么,您都必须声明您希望该列的null的占位符-您将在哪里执行此操作?甚至在知道是否、何时以及可以添加什么属性类型之前?另一方面,如果您只想在所有列中显示null的N/D,那么在客户端程序SQL Developer、SQL*Plus等中执行此操作要容易得多。@mathguy我不确定是否理解您的评论。每个属性类型(例如country)都有一个ID和一个名称,这是具有不同语义的不同列。当我对它们进行透视时,我希望生成的空值对于名称转换为N/D,对于ID转换为-1,这是数据仓库设计的要求。当给定ID的属性不存在时,我们如何应用此逻辑?在ID=2的示例中,我们没有颜色属性。我天真地认为PIVOT NVLMAXATTR_NAME,'N/D'作为名称将给出正确的结果,但这不起作用。您的尝试可以很容易地纠正。您所关注的必须始终是聚合函数,而nvl不是。诀窍是将nvl移动到max内部效率有点低,但它会起作用:pivot maxnvlattr_name,'N/D'/for。。。但这将在输出中的每一列中放置“N/D”而不是null。我的评论是,你似乎不想这样。根据列的不同,您希望null的替换内容有所不同。在将来的某个时候,您将添加一个新属性,该属性是在使用全新的null替换进行数据透视后的一个新列。您现在如何编写代码?