在Oracle中动态地将行旋转到列中

时间:2011-10-11 17:36:42

标签: sql oracle oracle10g pivot

我有以下名为_kv的Oracle 10g表:

select * from _kv

ID       K       V
----     -----   -----
  1      name    Bob
  1      age     30
  1      gender  male
  2      name    Susan
  2      status  married

我想使用纯SQL(而不是PL / SQL)将我的密钥转换为列,以便生成的表看起来像这样:

ID       NAME    AGE    GENDER  STATUS
----     -----   -----  ------  --------
  1      Bob      30     male 
  2      Susan                   married
  • 查询应该包含与表中存在的唯一K一样多的列(没有那么多)
  • 在运行查询之前,无法知道列可能存在哪些列。
  • 我试图避免运行初始查询以编程方式构建最终查询。
  • 空白单元格可能为空或空字符串,并不重要。
  • 我使用的是Oracle 10g,但11g解决方案也可以。

当您知道可以调用您的透视列时,有很多示例,但我无法为Oracle找到通用的透视解决方案。

谢谢!

4 个答案:

答案 0 :(得分:29)

Oracle 11g提供了PIVOT操作,可以执行您想要的操作。

Oracle 11g解决方案

select * from
(select id, k, v from _kv) 
pivot(max(v) for k in ('name', 'age', 'gender', 'status')

(注意:我没有11g的副本来测试这个,所以我还没有验证它的功能)

我从http://orafaq.com/wiki/PIVOT

获得了此解决方案

EDIT - pivot xml选项(也是Oracle 11g)
显然,当您不知道可能需要的所有可能的列标题时,还有一个pivot xml选项。 (请参阅位于http://www.oracle.com/technetwork/articles/sql/11g-pivot-097235.html的页面底部附近的 XML TYPE 部分)

select * from
(select id, k, v from _kv) 
pivot xml (max(v)
for k in (any) )

(注意:和以前一样,我没有11g的副本来测试它,所以我还没有验证它的功能)

Edit2:vpivot语句中的pivot xml更改为max(v),因为它应该按照其中一个提及进行汇总评论。我还添加了in子句,这对pivot不是可选的。当然,必须在in子句中指定值会使得完全动态的pivot / crosstab查询的目标失败,就像这个问题的海报一样。

答案 1 :(得分:6)

要处理可能存在多个值的情况(在您的示例中为v),我使用public class MainActivity extends ActionBarActivity { private EditText MyNumber; private ImageView MyImage; private ListView MyIndexList; private ArrayList<HashMap<String,String>> list = new ArrayList<>(); private ArrayList<HashMap<Integer,Bitmap>> list2 = new ArrayList<>(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); MyNumber = (EditText)findViewById(R.id.choiceofindex); MyImage = (ImageView)findViewById(R.id.imageRecup); MyIndexList = (ListView)findViewById(R.id.listIndex); } class GetImageTask extends AsyncTask<Integer,Integer,Bitmap> { protected Bitmap doInBackground(Integer... index) { HashMap<String,String> hashmap = new HashMap<>(); HashMap<Integer,Bitmap> hashmap2 = new HashMap<>(); Integer choix = index[0]; String urldisplay = "http://example.com/image.php?index=" + choix; Bitmap RecupImage = null; try { InputStream in = new java.net.URL(urldisplay).openStream(); RecupImage = BitmapFactory.decodeStream(in); } catch (Exception e){ Log.e("Error", e.getMessage()); e.printStackTrace(); } hashmap.put("index","index " +choix); hashmap2.put(choix,RecupImage); list.add(hashmap); list2.add(hashmap2); return RecupImage; } protected void onProgressUpdate(Integer... progress) { Context context = getApplicationContext(); super.onProgressUpdate(progress); Toast.makeText(context, "Wait...", Toast.LENGTH_LONG).show(); } protected void onPostExecute(Bitmap result) { Context context = getApplicationContext(); super.onPostExecute(result); MyImage.setImageBitmap(result); Toast.makeText(context, "Image!!", Toast.LENGTH_LONG).show(); } } public void onClickRecup(View v) { try{ int index= Integer.parseInt(MyNumber.getText().toString()); GetImageTask GetImage = new GetImageTask(); GetImage.execute(index); } catch (NumberFormatException nfe) { Context context = getApplicationContext(); Toast.makeText(context, "No image!", Toast.LENGTH_LONG).show(); } } public void onClickIndex(View v) { try { SimpleAdapter adapter = new SimpleAdapter(this,list,R.layout.row, new String[]{"index"}, new int[] {R.id.index}); MyIndexList.setAdapter(adapter); MyIndexList.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { Bitmap Imageback = list2.get(position).get(R.id.index); MyImage.setImageBitmap(Imageback); } }); } catch (Exception e){ Context context = getApplicationContext(); Toast.makeText(context, "Problem on ListView", Toast.LENGTH_LONG).show(); } } } PIVOT

LISTAGG

由于您需要动态值,因此在调用pivot语句之前,请使用动态SQL并传入在表数据上运行select所确定的值。

答案 2 :(得分:5)

发生在枢轴上的任务。以下对我的测试仅适用于11g:

select * from
(
  select ID, COUNTRY_NAME, TOTAL_COUNT from ONE_TABLE 
) 
pivot(
  SUM(TOTAL_COUNT) for COUNTRY_NAME in (
    'Canada', 'USA', 'Mexico'
  )
);

答案 3 :(得分:2)

首先,需要再次使用pivot xml进行动态调整。我们有另一种方法可以通过将列名存储在变量中并将它们传递给动态sql,如下所示。

考虑我们有一个如下表格。

enter image description here

如果我们需要将YR列中的值显示为列名称,并将QTY中的值显示在那些列中,那么我们可以使用以下代码。

declare
  sqlqry clob;
  cols clob;
begin
  select listagg('''' || YR || ''' as "' || YR || '"', ',') within group (order by YR)
  into   cols
  from   (select distinct YR from EMPLOYEE);


  sqlqry :=
  '      
  select * from
  (
      select *
      from EMPLOYEE
  )
  pivot
  (
    MIN(QTY) for YR in (' || cols  || ')
  )';

  execute immediate sqlqry;
end;
/

结果

enter image description here