将 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_categoriesEXEC : ? 我从来没有用过 DBVisualiser,所以不知道我害怕。 call 是一个有效的 SQL 关键字。 @exec 不是,但它们是 SQL*Plus 的一部分(@ 调用脚本,而不是过程,exec blahblahbegin 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的主要内容,如果未能解决你的问题,请参考以下文章

MS Access 查询转换为 Sql Server

转储 T-sql 脚本失败服务器 2000

将 XML 转换为表 SQL Server 2005

将计算的jet-sql访问列转换为sql server

将 T-SQL 转换为 MySQL

如何将 nvarchar 从 T-SQL 方言转换为 hiveQL?