在 oracle 中使用 sql 动态更改列标题
Posted
技术标签:
【中文标题】在 oracle 中使用 sql 动态更改列标题【英文标题】:Use pivot for dynamically changing column headers using sql in oracle 【发布时间】:2020-11-17 07:36:17 【问题描述】:我有一个需要对数据进行透视的要求。 然而,枢轴需要是动态的,因为列标题会根据列 app_id 不断变化。 所以如果 app_id=1。列标题将为 A、B、C、D,如果 app_id=2,列将为 CDEF 等等。 此外,每组值都有一个 id。因此对于 id、120 和 app_id=1 ,A、B、C、D 列将显示值等等。
当前示例数据只有 2 个 app_id,但可能还有更多,所以 app_id 和标签会不断变化,因此我需要编写一个动态查询。
示例表数据:
ID label value app_id
--- ----- ----- ------
120 A Alpha 1
120 B Beta 1
120 C Class 1
120 D Delta 1
120 C Alpha 2
120 D Beta 2
120 E Class 2
120 F Delta 2
建设性的查询是
WITH data( ID, label, value, app_id ) AS
(
SELECT 120, 'A', 'Alpha', 1 FROM dual UNION ALL
SELECT 120, 'B', 'Beta' , 1 FROM dual UNION ALL
SELECT 120, 'C', 'Class', 1 FROM dual UNION ALL
SELECT 120, 'D', 'Delta', 1 FROM dual UNION ALL
SELECT 120, 'C', 'Alpha', 2 FROM dual UNION ALL
SELECT 120, 'D', 'Beta' , 2 FROM dual UNION ALL
SELECT 120, 'E', 'Class', 2 FROM dual UNION ALL
SELECT 120, 'F', 'Delta', 2 FROM dual
)
SELECT *
FROM data
预期输出:
SELECT * FROM data WHERE ID = 120 AND app_id = 1;
app_id A B C D ID
------ ------ ----- ----- ----- -----
1 Alpha Beta Class Delta 120
SELECT * FROM data WHERE ID = 120 AND app_id = 2;
app_id C D E F ID
------ ------ ----- ----- ----- -----
2 Alpha Beta Class Delta 120
【问题讨论】:
【参考方案1】:你能做什么
SELECT *
FROM data
PIVOT
(
MAX(value) FOR label IN ('A' AS "A", 'B' AS "B",'C' AS "C",'D' AS "D")
)
WHERE ID = 120 AND app_id = 1
作为静态数据透视语句可能会转换为包含两个相应参数的函数
CREATE OR REPLACE FUNCTION Get_Pivoted_Labels( i_id data.id%type, i_app_id data.app_id%type )
RETURN SYS_REFCURSOR IS
v_recordset SYS_REFCURSOR;
v_sql VARCHAR2(32767);
v_cols VARCHAR2(32767);
BEGIN
SELECT LISTAGG( ''''||label||''' AS "'||label||'"' , ',' )
WITHIN GROUP ( ORDER BY label )
INTO v_cols
FROM ( SELECT DISTINCT label
FROM data
WHERE ID = i_id AND app_id = i_app_id );
v_sql :=
'SELECT *
FROM data
PIVOT
(
MAX(value) FOR label IN ( '|| v_cols ||' )
)
WHERE ID = :id AND app_id = :aid';
OPEN v_recordset FOR v_sql USING i_id, i_app_id;
RETURN v_recordset;
END;
/
其中一个辅助查询,其中明确选择了label
列,用于确定要连接到主 SQL 字符串的字符串(v_cols
for 'A' AS "A", 'B' AS "B",'C' AS "C",'D' AS "D"
)以便在返回 SYS_REFCURSOR
类型值的游标。
并被调用
VAR rc REFCURSOR
VAR v_id NUMBER
VAR v_app_id NUMBER
EXEC :rc := Get_Pivoted_Labels(:v_id,:v_app_id);
PRINT rc
来自 SQL 开发人员的控制台。
Demonstration 带有生成的 SQL 语句
如果 SELECT 列表中的列顺序很重要,请使用下面的代码来创建函数
CREATE OR REPLACE FUNCTION Get_Pivoted_Labels( i_id data.id%type, i_app_id data.app_id%type )
RETURN SYS_REFCURSOR IS
v_recordset SYS_REFCURSOR;
v_sql VARCHAR2(32767);
v_cols_1 VARCHAR2(32767);
v_cols_2 VARCHAR2(32767);
BEGIN
SELECT LISTAGG( ''''||label||''' AS "'||label||'"' , ',' )
WITHIN GROUP ( ORDER BY label ),
LISTAGG( label , ',' )
WITHIN GROUP ( ORDER BY label )
INTO v_cols_1, v_cols_2
FROM ( SELECT DISTINCT label, value
FROM data
WHERE ID = i_id AND app_id = i_app_id );
v_sql :=
'SELECT ID, '|| v_cols_2 ||', app_id
FROM data
PIVOT
(
MAX(value) FOR label IN ( '|| v_cols_1 ||' )
)
WHERE ID = :id AND app_id = :aid';
OPEN v_recordset FOR v_sql USING i_id, i_app_id;
RETURN v_recordset;
END;
/
【讨论】:
正如 OP 在他的 cmets 中对我的回答所说,最终的“客户端”是 Oracle APEX,它是少数不能使用 REF CURSOR 的环境之一。 感谢@StewAshton,但随后需要阅读每条评论,即使我自己的答案或问题下方不存在:) 为什么是“但是”?我从没想过你会阅读对别人问题的每一条评论。这就是我花时间通知你的原因。为什么人们会像批评一样试图告知? 好吧@StewAshton,您的通知没问题,而OP希望清楚地说明他在问题中需要什么。这样我们就不会在一些无用的问题上浪费时间。【参考方案2】:这是一个很常见的问题,原因很简单,答案是“否”。
使用 Oracle 数据库,每个 SELECT 语句都必须具有固定且已知的“形状”(列数、名称和数据类型)。在最近的版本中,有“多态表函数”似乎打破了这个规则,但实际上它们并没有:“形状”是在解析语句时计算的,因此在执行开始之前它是固定的和已知的。
您不想要“多态”(在解析时改变形状),您想要真正的“动态”(在执行时根据数据改变形状)。 Oracle 不这样做。
您可以使用一条 SQL 语句获得的最接近的结果是准确输出包含 XML 或 JSON 的 一个 列。然后调用数据库的程序将负责将该结果转换为行和列。
另一种选择是执行一个 SELECT 来获取列名并生成第二个 SELECT 来获得您想要的结果。我写了一个函数来帮助做到这一点: https://stewashton.wordpress.com/2018/05/30/improved-pivot-function/
我不会演示这些替代方案,因为它们不会直接回答您的问题。您的问题没有直接答案。
【讨论】:
这可以使用数组/集合来完成吗?只是问,因为我对替代方案一无所知。 您问“这可以使用数组/集合来完成吗?”我不明白你的意思,因为数组/集合也有固定的形状。首先告诉我什么软件在调用数据库:它是一个应用程序吗?是 SQL*Plus 吗?它是某种报告工具吗?可能的替代方案取决于您的答案。 SQL Developer-> Oracle 12.1 不,我的意思是一旦你完成了开发,查询会发生什么?它将在哪里部署到生产环境中?那么什么程序会执行呢? 此查询将用作 oracle apex 应用程序中的数据源以上是关于在 oracle 中使用 sql 动态更改列标题的主要内容,如果未能解决你的问题,请参考以下文章
使用 Sql Developer Oracle 的动态数据透视查询