Is there a SQL statement that will break what would be 2 long columns into several pairs of columns?

自作多情 提交于 2020-01-05 07:13:12

问题


I have data that, in a "normal" query, would appear like this:

Val1    Val2
----    ----
1   2
2   [blank]
3   2
4
5   1
6   3
..
96  1

What I want, though, is something like this (I need to limit the number of rows to 12):

Val1    Val2    Val1    Val2    Val1    Val2    ... Val1    Val2
----    ----    ----    ----    ----    ----
1   2   13  1   25  [blank]  ...    85  1
2   [blank] 14  1   26  3   ... 86  [blank]
..  ... ... ... ... ... ... ... ...
12  1   24  [blank] 36  2   ... 96  3

Is there a select statement that would give me that? I'm no SQL expert, but I'm thinking something (semantically) along these lines:

select (select val1, val2 from dbtable where val1 < 13),
(select val1, val2 from dbtable where val1 > 12 and val1 < 25),
...
(select val1, val2 from dbtable where val1 > 84)
from dbtable

UPDATE

In response to dfb's sql example:

When I do this:

SELECT t1.Val1, t1.Val2 FROM 
(SELECT Val1, Val2, rownum() as rownum FROM dbTable) t1 
INNER JOIN (SELECT Val1, Val2, rownum() as rownum FROM dbTable) t2 
ON t1.rownum/2 == t2.rownum/2

...I get "FROM keyword not found where expected"

And when I do this (remove the "rownum()" stuff):

SELECT t1.Val1, t1.Val2 FROM 
(SELECT Val1, Val2 FROM dbTable) t1 
INNER JOIN (SELECT Val1, Val2 FROM dbTable) t2 
ON t1.rownum/2 == t2.rownum/2

...I get "ORA-01747: invalid user.table.column, table.column, or column specification"

UPDATE 2

Sully's example came the closest, although I wish the UNION SQL would work - it would be better if it could be done without pushing down the valid values. As it is, I have the right layout but the vals are not appearing just where I need them to within that 16X12 layout. At any rate, for posterity's sake, here's how the Rows and Columns are dynamically created (not as shown in the code below, and not identical to each other):

//prebuild 12 rows in outputDt 
int iRows = 12;
while (iRows > 0)
{
    DataRow row = outputDt.NewRow();
    outputDt.Rows.Add(row);
    iRows -= 1;
}

//prebuild 16 cols in outputDt 
int iCols = 16;
while (iCols > 0) {
    DataColumn col = new DataColumn();
    outputDt.Columns.Add(col);
    iCols -= 1;
}

FINAL UPDATE

Got it working. See Is it possible to populate a DataGridView with alternating vertical columns?


回答1:


Bonus to this is if you end up with more data, it'll just build more horizontal columns as needed but never go over 12 rows of data. The "in-SQL" way will require code changes if you ever need to display more data.

Disclaimer: This is totally off-the-cuff (C# as that's what I'm used to). There are probably much better ways to do this (Linq?) The logic should be pretty close, but this gives you the flexibility to use that list of data for other purposes than this very narrowly focused display.

DataTable dt = ResultsFromSproc();
DataTable outputDt = new DataTable();

//prebuild 12 rows in outputDt
int iRows = 12;
while(iRows > 0) {
    outputDt.Rows.Add(new DataRow());
    iRows-=1;
}

int outputColumn = 0;
for(int i = 0; i < dt.Rows.Count; i+=1){
    DataRow dr = dt.Rows[i];

    if(i % 12 == 0 && i > 0) { 
        //add two more columns to outputDt
        outputDt.Columns.Add() //Not sure but you might need to give it a name. (outputColumn+2).ToString() should work
        outputDt.Columns.Add() //Not sure but you might need to give it a name. (outputColumn+3).ToString() should work
        outputColumn += 1;
    }
    outputDt.Rows[i%12][outputColumn] = dr[0];
    outputDt.Rows[i%12][outputColumn + 1] = dr[1];
}
//Step2: Bind to outputDt. Step 3: Profit!

ALTERNATE version: For requirement that val1 == 48 goes in cell 48 (see comments)

DataTable dt = ResultsFromSproc();
DataTable outputDt = new DataTable();

//prebuild 12 rows in outputDt
int iRows = 12;
while(iRows > 0) {
    outputDt.Rows.Add(new DataRow());
    iRows-=1;
}

int outputColumn = 0;
int iMaxCell = (int)dt.Select("MAX(Val1)")[0][0];
//ASSUMING YOU HAVE ALREADY DONE AN ORDER BY Val1 in SQL (if not you need to sort it here first)
for(int i = 0; i < iMaxCell; i+=1){
    DataRow dr = dt.Rows[i];

    if(i % 12 == 0 && i > 0) { 
        //add two more columns to outputDt
        outputDt.Columns.Add() //Not sure but you might need to give it a name. (outputColumn+2).ToString() should work
        outputDt.Columns.Add() //Not sure but you might need to give it a name. (outputColumn+3).ToString() should work
        outputColumn += 2;
    }
    //compare to i+1 if your data starts at 1
    if((int)dr[0] == (i+1)){
        outputDt.Rows[i%12][outputColumn] = dr[0];
        outputDt.Rows[i%12][outputColumn + 1] = dr[1];
    }
}
//Step2: Bind to outputDt. Step 3: Profit!



回答2:


if you know the Rows will always be 96 then this is one of the static Pivoting technique

select val1 AS col1, val2 AS col2,NULL AS Col3,NULL AS Col4 ,NULL AS Col5,NULL AS Col6,NULL AS col7,NULL AS Col8 from dbtable where val1 < 13
UNION 
select NULL AS col1,NULL AS col2 ,val1 AS Col3, val2 AS Col4,NULL AS Col5,NULL AS Col6,NULL AS col7,NULL AS Col8 from dbtable where val1 > 12 and val1 < 25
UNION
select NULL AS col1,NULL AS col2 ,NULL AS Col3, NULL AS Col4,val1 AS Col5,val2 AS Col6,NULL AS col7,NULL AS Col8 from dbtable where val1 > 25 and val1 < 37
.....
...AND So ON

And if the number of rows are unknown then you needs to do Dynamic Pivoting.

Note: above syntax is for SQL Server 2008, I hope it wont be different for Oracle.




回答3:


You can do something like this this, but it's not pretty. The comments about doing this in a presentation layer are on point.

SELECT ... FROM
(SELECT v1,v2,rownum as rn FROM Foo WHERE mod(rownum,2)=1) t1
INNER JOIN (SELECT v1,v2,rownum as rn FROM Foo WHERE mod(rownum,2)=0) t2
ON (t1.rn/2 = t2.rn/2)


来源:https://stackoverflow.com/questions/12078637/is-there-a-sql-statement-that-will-break-what-would-be-2-long-columns-into-sever

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!