如何将值从宽格式转换为窄格式,将值用作过滤器?

时间:2015-07-29 11:21:02

标签: sql oracle plsql

我得到一张表X(有一行):

COL_XA   COL_VG   COL_LF  COL_EQ COL_PP COL_QM ... 
  1        0        0       0      1      1

每列COL_x只能包含值0或1.

我想将此表格转换为此格式Y

NAME
"COL_XA"
"COL_PP"
"COL_QM"
...

此表应仅打印表X中第一行(也是唯一一行)的值为1的列。

这个问题与任何其他关于换位的问题有关,区别在于我不想要实际值,而是列名,这些名称是事先不知道的。

我可以使用Excel或PL / SQL创建表单的字符串列表 MIN(CASE WHEN t.COL_XA = 1 THEN 'COL_XA' ELSE null END) as NAME,但此解决方案效率低下(EXECUTE IMMEDIATE)并且难以维护。传递给EXECUTE IMMEDIATE的字符串限制为32700个字符,在生产中很容易超出,而表X可以有超过500个字段。

3 个答案:

答案 0 :(得分:1)

这是解决方案,但我必须将其分为两部分

首先提取表的所有列名。我用LISTAGG来收集分隔的列名, 我将在第二个查询中使用第一个查询的输出。

select listagg(column_name,',') WITHIN GROUP (ORDER BY column_name ) 
from user_tab_cols where upper(table_name)='X' 

上述查询的输出将类似于COL_XA,COL_VG,COL_LF,COL_EQ,COL_PP,COL_QM ......等等。

复制上面的输出并用于下面的查询替换

select NAME from X
unpivot ( bit for NAME in (<outputvaluesfromfirstquery>))
where bit=1

我正在尝试合并上面的两个,但我有选择pivot xml但不适用于unpivot xml。

答案 1 :(得分:1)

要完全自动化查询,您必须能够读取实际游标的列名。在PL / SQL中,这可以使用DBMS_SQL(其他方式将在JDBC中)。基于this OTN thread这里的基本表函数。

重要部分是

1) dbms_sql.parse 以文本字符串形式提供的查询和 dbms_sql.execute

2) dbms_sql.describe_columns 以获取从表x上的查询返回的列名列表

3) dbms_sql.fetch_rows 获取第一行

4)循环列并检查 dbms_sql.column_value ,如果等于1输出column_name(使用PIPE)

 create or replace type str_tblType as table of varchar2(30);
 /

 create or replace function get_col_name_on_one  return  str_tblType
 PIPELINED
 as
  l_theCursor     integer default dbms_sql.open_cursor;
  l_columnValue   varchar2(2000);
  l_columnOutput  varchar2(4000);
  l_status        integer;
  l_colCnt        number default 0;
  l_colDesc       dbms_sql.DESC_TAB; 
 begin
     dbms_sql.parse( l_theCursor,  'SELECT * FROM X', dbms_sql.native );
     for i in 1 .. 1000 loop
          begin
               dbms_sql.define_column( l_theCursor, i,
                                       l_columnValue, 2000 );
               l_colCnt := i;
           exception
              when others then
                  if ( sqlcode = -1007 ) then exit;
                  else
                       raise;
                  end if;
         end;          
      end loop;   
      dbms_sql.define_column( l_theCursor, 1, l_columnValue, 2000 );
      l_status := dbms_sql.execute(l_theCursor);  
      dbms_sql.describe_columns(l_theCursor,l_colCnt, l_colDesc);

      if dbms_sql.fetch_rows(l_theCursor) > 0 then
      for lColCnt in 1..l_colCnt
      loop
         dbms_sql.column_value( l_theCursor, lColCnt, l_columnValue );
         --DBMS_OUTPUT.PUT_LINE( l_columnValue);
         IF (l_columnValue = '1') THEN
            DBMS_OUTPUT.PUT_LINE(Upper(l_colDesc(lColCnt).col_name));
            pipe row(Upper(l_colDesc(lColCnt).col_name));
         END IF;   
      end loop;
      end if; 
 return;
 end;
 /

 select * from table(get_col_name_on_one);

 COLUMN_LOOOOOOOOOOOOOONG_100
 COLUMN_LOOOOOOOOOOOOOONG_200
 COLUMN_LOOOOOOOOOOOOOONG_300
 COLUMN_LOOOOOOOOOOOOOONG_400
 COLUMN_LOOOOOOOOOOOOOONG_500
 COLUMN_LOOOOOOOOOOOOOONG_600
 COLUMN_LOOOOOOOOOOOOOONG_700
 COLUMN_LOOOOOOOOOOOOOONG_800
 COLUMN_LOOOOOOOOOOOOOONG_900
 COLUMN_LOOOOOOOOOOOOOONG_1000

使用此解决方案时,您不应该遇到使用宽表的麻烦,我使用带有列名称的1000列表进行了测试。

答案 2 :(得分:0)

你可以使用一堆resources :trips root 'trips#index' s来执行此操作:

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.CardView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="160dp"
    android:layout_height="200dp"
    android:layout_gravity="top|left"
    android:layout_weight="1"
    android:layout_margin="8dp"
    app:cardCornerRadius="0dp"
    app:elevation="5dp">

    <ImageView
        android:id="@+id/avatar"
        android:layout_width="160dp"
        android:layout_height="160dp"
        android:layout_gravity="top|center"
        android:scaleType="centerCrop"
        android:src="@drawable/wp" />

    <TextView
        android:id="@+id/album_name"
        android:layout_width="match_parent"
        android:layout_height="25dp"
        android:alpha="0.85"
        android:background="@color/light"
        android:layout_above="@+id/detail"
        android:layout_below="@+id/avatar"
        android:gravity="bottom|center"
        android:layout_alignParentBottom="true"
        android:text="Test"
        android:textColor="@color/cardview_dark_background" />
    <TextView
        android:id="@+id/detail"
        android:layout_width="match_parent"
        android:layout_height="15dp"
        android:layout_gravity="bottom|center"
        android:alpha="0.85"
        android:background="@color/light"
        android:gravity="bottom|center"
        android:layout_alignParentBottom="true"
        android:layout_below="@+id/album_name"
        android:text="Test"
        android:textColor="@color/dim_foreground_disabled_material_dark" />


</android.support.v7.widget.CardView>
</LinearLayout>

编辑:

如果您只有一行,那么您不需要:

union all

您可以简单地使用:

select 'COL_XA' as name from table t where col_xa = 1 union all
select 'COL_VG' as name from table t where col_vg = 1 union all
. . .

一行不需要 MIN(CASE WHEN t.COL_XA = 1 THEN 'COL_XA' ELSE null END) as NAME (CASE WHEN t.COL_XA = 1 THEN 'COL_XA' END) 是多余的。