【问题标题】:Is there a SQL statement that will break what would be 2 long columns into several pairs of columns?是否有一条 SQL 语句可以将 2 个长列分成几对列?
【发布时间】:2023-04-10 09:04:01
【问题描述】:

我的数据在“正常”查询中会如下所示:

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

不过,我想要的是这样的(我需要将行数限制为 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

是否有一个 select 语句可以给我这个?我不是 SQL 专家,但我在想一些事情(语义上)沿着这些思路:

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

更新

响应dfb的sql示例:

当我这样做时:

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

...我得到 “FROM 关键字未在预期的位置找到”

当我这样做时(删除“rownum()”的东西):

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

...我得到 "ORA-01747: 无效的 user.table.column、table.column 或列 规范"

更新 2

Sully 的例子是最接近的,虽然我希望 UNION SQL 能够工作 - 如果它可以在不按下有效值的情况下完成会更好。事实上,我有正确的布局,但 val 并没有出现在 16X12 布局中我需要它们的位置。无论如何,为了后代的缘故,下面是动态创建行和列的方式(不是如下代码所示,并且彼此不完全相同):

//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;
}

最终更新

搞定了。见Is it possible to populate a DataGridView with alternating vertical columns?

【问题讨论】:

  • 有趣的问题...请问输出将用于什么?在我看来,这应该通过代码中的格式化逻辑而不是 SQL 来处理。
  • 是的,如果列名对于所有后续列都将保留 Val1 和 Val2,那么这是一个格式问题,应该在显示逻辑中处理。否则你可能需要阅读透视技术
  • @BLSully:这将在 DataGridView (C# Winforms) 中呈现。垂直呈现数据是有问题的。我认为从 git-go 中获取所需形式的数据会容易得多。
  • @ClearLogic:我不关心标题/标题;事实上,它们将是不可见的;只有数据本身应该显示在 DataGridView 中。

标签: sql oracle select nested


【解决方案1】:

这样做的好处是,如果您最终获得更多数据,它只会根据需要构建更多水平列,但绝不会超过 12 行数据。如果您需要显示更多数据,“in-SQL”方式将需要更改代码。

免责声明:这完全是即兴的(C#,因为那是我习惯的)。可能有更好的方法来做到这一点(Linq?)逻辑应该非常接近,但这使您可以灵活地将该数据列表用于这个非常狭窄的显示之外的其他目的。

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!

替代版本:要求 val1 == 48 进入单元格 48(参见 cmets)

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!

【讨论】:

  • 这可能行得通,谢谢。顺便说一句,懒惰是/不是问题。
  • 在“outputDt.Rows[i%12][outputColumn] = dr[0];”我得到,“位置 0 没有行。”
  • @ClayShannon:我没有编写每一行代码...用一个循环替换 //prebuild 12 rows... 注释,该循环迭代 12 次,将 DataRow 添加到 outputDt(请参阅该区域的编辑以回答)
  • 我认为这是正确的做法。将此表格式化逻辑保留在 SQL 代码之外,并在将其绑定到数据网格之前在 C# 中重新排列。
  • @BLSully:在调用 new DataRow() 时“System.Data.DataRow.DataRow(System.Data.DataRowBuilder)' 由于其保护级别而无法访问”
【解决方案2】:

你可以这样做,但它并不漂亮。关于在表示层中执行此操作的方法很重要。

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)

【讨论】:

  • 谢谢,但不行;我会在上面添加我的回复。
  • 看看这是否适合你,这是一般的想法——我修正了语法。我无法让它为 SQLFiddle 正确返回并且没有 Oracle 实例。一旦我在这里收到答案,我会更新 - stackoverflow.com/questions/12079909/…
  • 用什么代替省略号? v1 和 v2,IOW "SELECT v1,v2 FROM (..." ?
【解决方案3】:

如果您知道行数总是 96,那么这是静态旋转技术之一

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

如果行数未知,则需要进行动态透视。

注意:以上语法适用于 SQL Server 2008,希望 Oracle 不会有所不同。

【讨论】:

  • 我认为这非常接近(但还没有“雪茄”);这给了我所需的列数(实际上是 16,而不是 8 - 我扩展了示例),但是有 264 行(它需要是 12)。第 3 列和第 4 列中的 val 直到第 13 行才开始,第 5 列和第 6 列中的 val 直到第 25 行才开始,等等。有没有办法消除空单元格,以便所有返回的 val 显示在第 12 行行?
  • IOW,返回的数据是正确的,我只需要空/空单元格“离开” - 将填充的“单元格”“拉”到顶部。
猜你喜欢
  • 2021-08-19
  • 2022-01-08
  • 1970-01-01
  • 1970-01-01
  • 2023-03-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-01-07
相关资源
最近更新 更多