将 SQL Server T-SQL 转换为 SQL
Posted
技术标签:
【中文标题】将 SQL Server T-SQL 转换为 SQL【英文标题】:Convert SQL Server T-SQL to SQL 【发布时间】:2020-10-02 13:52:04 【问题描述】:我一直在尝试使用pivot
从表格中获取特定视图的代码。
create table temp
(
date datetime,
category varchar(3),
amount money
)
insert into temp values ('1/1/2012', 'ABC', 1000.00)
insert into temp values ('2/1/2012', 'DEF', 500.00)
insert into temp values ('2/1/2012', 'GHI', 800.00)
insert into temp values ('2/10/2012', 'DEF', 700.00)
insert into temp values ('3/1/2012', 'ABC', 1100.00)
DECLARE @cols AS NVARCHAR(MAX),
@query AS NVARCHAR(MAX);
SET @cols = STUFF((SELECT distinct ',' + QUOTENAME(c.category)
FROM temp c
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set @query = 'SELECT date, ' + @cols + ' from
(
select date
, amount
, category
from temp
) x
pivot
(
max(amount)
for category in (' + @cols + ')
) p '
execute(@query)
drop table temp
declare
语句中出现错误,并且pivot
未被 dbviz SQL 指挥官识别为关键字
我什至删除了@
并尝试运行它,但我无法成功执行
我正在使用 Oracle Database 11g(oracle JDBC 驱动程序)
【问题讨论】:
这绝对不是 oracle 语法。你能告诉我们你将这个 tsql 转换为 plsql 的努力吗? 您使用的是哪个版本的 Oracle? 与其尝试将 T-SQL 代码转换为 Oracle 代码,不如在给定一组示例数据的情况下显示预期的输出,这会容易得多。 我使用的是 Oracle db,但我们使用 SQL 编写查询。pivot
不被视为关键字。我不明白为什么
【参考方案1】:
如果您尝试从 SQLPlus 或 SQLcl 运行此代码,您可以使用下面的代码在 Oracle 中生成相同的信息。
Oracle 19c 及更高版本
在 Oracle 19 中,Oracle 引入了执行 LISTAGG(DISTINCT <value>)
的功能,从而无需执行子查询来删除任何重复项。
CREATE TABLE temp
(
date_val DATE,
category VARCHAR (3),
amount NUMBER
);
INSERT INTO temp
VALUES (DATE '2012-01-01', 'ABC', 1000.00);
INSERT INTO temp
VALUES (DATE '2012-02-01', 'DEF', 500.00);
INSERT INTO temp
VALUES (DATE '2012-02-01', 'GHI', 800.00);
INSERT INTO temp
VALUES (DATE '2012-02-10', 'DEF', 700.00);
INSERT INTO temp
VALUES (DATE '2012-03-01', 'ABC', 1100.00);
column category_list NEW_VALUE categories
SELECT '''' || LISTAGG (DISTINCT category, ''',''') WITHIN GROUP (ORDER BY category) || '''' AS category_list
FROM temp;
SELECT *
FROM (SELECT date_val, amount, category FROM temp) x
PIVOT (MAX (amount) FOR category IN (&categories)) p;
DROP TABLE temp;
早于 Oracle 19c
CREATE TABLE temp
(
date_val DATE,
category VARCHAR (3),
amount NUMBER
);
INSERT INTO temp
VALUES (DATE '2012-01-01', 'ABC', 1000.00);
INSERT INTO temp
VALUES (DATE '2012-02-01', 'DEF', 500.00);
INSERT INTO temp
VALUES (DATE '2012-02-01', 'GHI', 800.00);
INSERT INTO temp
VALUES (DATE '2012-02-10', 'DEF', 700.00);
INSERT INTO temp
VALUES (DATE '2012-03-01', 'ABC', 1100.00);
column category_list NEW_VALUE categories
SELECT '''' || LISTAGG (category, ''',''') WITHIN GROUP (ORDER BY category) || '''' AS category_list
FROM (SELECT DISTINCT category
FROM temp);
SELECT *
FROM (SELECT date_val, amount, category FROM temp) x
PIVOT (MAX (amount) FOR category IN (&categories)) p;
DROP TABLE temp;
任一脚本的结果
SQL> @test_tsql_conversion.sql
Table TEMP created.
1 row inserted.
1 row inserted.
1 row inserted.
1 row inserted.
1 row inserted.
CATEGORY_LIST
____________________
'ABC','DEF','GHI'
old:SELECT *
FROM (SELECT date_val, amount, category FROM temp) x
PIVOT (MAX (amount) FOR category IN (&categories)) p
new:SELECT *
FROM (SELECT date_val, amount, category FROM temp) x
PIVOT (MAX (amount) FOR category IN ('ABC','DEF','GHI')) p
DATE_VAL 'ABC' 'DEF' 'GHI'
____________ ________ ________ ________
01-FEB-12 500 800
10-FEB-12 700
01-JAN-12 1000
01-MAR-12 1100
Table TEMP dropped.
【讨论】:
如果我们要动态使用值而不是硬编码列值,我们应该创建一个过程吗? 因为最终选择是在做SELECT *
,如果有任何额外的类别,它们会出现在列列表中。您可以通过添加带有另一个类别的附加插入语句来测试它。
不,我的意思是从类别变量中动态添加新列,而不对值进行硬编码
我不确定你的意思。如果存在其他类别,它们将作为列动态添加到结果中,因为使用了SELECT *
。脚本中没有任何地方(除了插入语句)类别是硬编码的。列数取决于数据集中存在的类别数。
SELECT * FROM (SELECT date_val, amount, category FROM temp) x PIVOT (MAX (amount) FOR category IN (&categories)) p;
不应该是category _list
【参考方案2】:
基于 EJ Egyed 的回答,这里有一个 PL/SQL 版本:
declare
cols long;
query long;
results sys_refcursor;
begin
select '''' || listagg(category||''' as "'||upper(category)||'"', ', ''') within group (order by category)
into cols
from (select distinct category from temp);
query :=
'select * from
( select dt, amount, category
from temp ) x
pivot
( max(amount) for category in ('||cols||') ) p';
open results for query;
dbms_sql.return_result(results);
end;
解决方案将根据您希望如何执行代码而有所不同。 dbms_sql.return_result
会将结果集传递给 12c 或更高版本的客户端,例如 SQL*Plus 或 SQL Developer,但可能无法与 DbVisualizer 或 Tableau 等第三方工具一起使用。 SQL*Plus 是 Oracle 的基本命令行工具,主要用于管理和部署脚本,但并不真正适合最终用户报告。 SQL Developer 是一个程序员的 IDE,业务分析师可能会使用它,但它同样不适合一般最终用户。
最通用的方法可能是将其编写为返回一个引用的过程,然后可以由用 Java、C# 等编写的应用程序处理。
过程版本如下所示:
create or replace procedure pivot_categories
( out_results out sys_refcursor )
as
cols long;
query long;
begin
select '''' || listagg(category||''' as "'||upper(category)||'"', ', ''') within group (order by category)
into cols
from (select distinct category from temp);
query :=
'select * from
( select dt, amount, category
from temp ) x
pivot
( max(amount) for category in ('||cols||') ) p';
open out_results for query;
end pivot_categories;
【讨论】:
我很难在 DB-viz 中运行它。代码看起来不错。我把它写成一个函数。在函数定义之后,下一行DECLARE
抛出一个错误。long
也用于数字,或者也可以用来代替varchar2
?
另外,within group (order by category) into cols
部分抛出错误,提示 PLS-00103:在预期以下情况之一时遇到符号“GROUP”:,从批量处理
一个函数中可能不应该有任何declare
关键字。它返回什么,你怎么称呼它? PL/SQL 的long
是写VARCHAR2(32760)
的简写方式,与数字无关。它应该被弃用,因为它与 clob
出现之前的旧的、过时的 long
列类型相关联,但实际上它与它无关。
感谢您的回复。我试过你的程序,编译成功。现在通过命令编辑器如何调用它? @call pivot_categories
或 EXEC :
?
我从来没有用过 DBVisualiser,所以不知道我害怕。 call
是一个有效的 SQL 关键字。 @
和 exec
不是,但它们是 SQL*Plus 的一部分(@
调用脚本,而不是过程,exec blahblah
是 begin blahblah; end;
的快捷方式)因此可能会转移到 DBVis 中,或者他们可能不会。我看到DB Visualizer User Guide 中使用了@call
,所以也许你需要从那里开始。【参考方案3】:
这是你需要的:
create table temp
(
date timestamp(3),
category varchar2(3),
amount number
);
insert into temp values ('1/1/2012', 'ABC', 1000.00)
insert into temp values ('2/1/2012', 'DEF', 500.00)
insert into temp values ('2/1/2012', 'GHI', 800.00)
insert into temp values ('2/10/2012', 'DEF', 700.00)
insert into temp values ('3/1/2012', 'ABC', 1100.00)
v_cols NVARCHAR(MAX);
v_query NVARCHAR(MAX);
SELECT STUFF((SELECT distinct ',' || QUOTENAME(c.category)
FROM temp c
FOR INTO v_cols FROM dual XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
v_query := 'SELECT date, ' || v_cols || ' from
(
select date
, amount
, category
from temp
) x
pivot
(
max(amount)
for category in (' || v_cols || ')
) p '
execute immediate v_query
drop table temp
还有一个在线sql转换工具:http://www.sqlines.com/online
【讨论】:
不,我没有尝试转换并尝试运行它。我使用的是 DB viz 专业版。我不确定这是 DBVIZ 错误还是它没有运行的代码中有错误。我正在使用 DBVIz 的 SQL 指挥官来运行代码 Oracle没有stuff()
函数或者NVARCHAR(MAX)
,FOR INTO
不可能是对的,from dual xml path(''),type)
似乎没有任何意义。以上是关于将 SQL Server T-SQL 转换为 SQL的主要内容,如果未能解决你的问题,请参考以下文章