将Postgres交叉表查询转换为TSQL

时间:2019-03-03 18:48:18

标签: sql sql-server tsql pivot

我要实现以下目标:

一个数据库,用于存储不同语言的翻译。一次查询即可获取所需语言的所有字符串,如果不存在该翻译,则使用次优语言作为后备等。对于每种语言,后备可能会有所不同(例如FR-DE-EN与DE-EN-FR) 。

我已经通过一个交叉表查询使用pgsql实现了它,并且想将其转换为SQL Server,但是有点卡在了那里。我认为PIVOT是实现我期望的结果的语言功能,但尚未弄清楚如何正确使用它。

MWE定义和测试数据:

-- load tablefunc extension for crosstab
drop extension if exists tablefunc;
create extension tablefunc;
-- crosstab only allows single column - define int and varchar tuples for this purpose
DROP TYPE IF EXISTS intT; CREATE TYPE intT AS  (module int, id int );
DROP TYPE IF EXISTS strT; CREATE TYPE strT AS (lang varchar, txt varchar);

drop table if exists texts;
drop table if exists langs;

create table texts
(   module int not null
,   id int not null
,   lang varchar not null
,   txt varchar not null);

create table langs -- for each language (first) store up to 3 languages (lang) and their priority (lower = would be used first)
(   first varchar not null
,   lang varchar not null
,   priority int not null);

insert into texts (module, id, lang, txt) values
    (0,0,'def','HelloDEF'),
    (0,1,'def','WorldDEF'),
    (0,0,'en','Hello'),
    (0,1,'en','World'),
    (0,0,'de','Hallo'),
    (0,1,'de','Welt'),
    (0,0,'jp','Konnichiwa'),
    (0,1,'fr','Monde'),
    (1,0,'def','Switzerland'),
    (1,0,'de','Schweiz'),
    (1,0,'fr','Suisse'),
    (1,0,'jp','Suisu');

insert into langs (first, lang, priority) values
    ('jp','jp',0),
    ('jp','en',1),
    ('jp','def',2),
    ('en','en',0),
    ('en','def',1),
    ('en','def',2),
    ('de','de',0),
    ('de','en',1),
    ('de','def',2),
    ('fr','fr',0),
    ('fr','de',1),
    ('fr','def',2);

查询(pgsql):

select (mod_id).*, (coalesce(a,b,c)).* -- unpack tuple types here to get nice table
from crosstab($$
    select (module,id) as mod_id, priority, (lang,txt) as lang_txt
    from texts
    join langs using (lang)
    where first = 'fr' --! language goes here
    and module = 0 --! module integer goes here
    order by id, priority asc
$$,$$
    select generate_series(0,2) -- always return 0,1,2 here.
$$) as ct (mod_id intT, a strT, b strT, c strT);

输出:

 module | id | lang |  txt
--------+----+------+-------
      0 |  0 | de   | Hallo
      0 |  1 | fr   | Monde

1 个答案:

答案 0 :(得分:3)

据我了解的问题,使用标准SQL即可实现此结果,而无需旋转数据。简单的create table #texts ( module int not null , id int not null , lang varchar(50) not null , txt varchar(50) not null); create table #langs -- for each language (first) store up to 3 languages (lang) and their priority (lower = would be used first) ( first varchar(50) not null , lang varchar(50) not null , priority int not null); insert into #texts (module, id, lang, txt) values (0,0,'def','HelloDEF'), (0,1,'def','WorldDEF'), (0,0,'en','Hello'), (0,1,'en','World'), (0,0,'de','Hallo'), (0,1,'de','Welt'), (0,0,'jp','Konnichiwa'), (0,1,'fr','Monde'), (1,0,'def','Switzerland'), (1,0,'de','Schweiz'), (1,0,'fr','Suisse'), (1,0,'jp','Suisu'); insert into #langs (first, lang, priority) values ('jp','jp',0), ('jp','en',1), ('jp','def',2), ('en','en',0), ('en','def',1), ('en','def',2), ('de','de',0), ('de','en',1), ('de','def',2), ('fr','fr',0), ('fr','de',1), ('fr','def',2); 就足够了。下面的查询将在SQL Server和Postgres中工作。

样本数据

ROW_NUMBER

查询

我接受了您的内部查询,并在其中添加了priority。显然,我们只需要为每个id只选择最高PARTITION BY id的行(这就是ORDER BY priority中有ROW_NUMBERmodule的原因定义)。如果您想一次获得多个module的结果,而不仅仅是一个特定模块的结果,请在PARTITION BY子句中添加SELECT #texts.module ,#texts.id ,#langs.priority ,#langs.lang ,#texts.txt ,ROW_NUMBER() OVER (PARTITION BY #texts.id ORDER BY #langs.priority) AS rn FROM #texts INNER JOIN #langs ON #langs.lang = #texts.lang WHERE #langs.first = 'fr' --! language goes here AND #texts.module = 0 --! module integer goes here ORDER BY #texts.id, #langs.priority asc ;

+--------+----+----------+------+----------+----+
| module | id | priority | lang |   txt    | rn |
+--------+----+----------+------+----------+----+
|      0 |  0 |        1 | de   | Hallo    |  1 |
|      0 |  0 |        2 | def  | HelloDEF |  2 |
|      0 |  1 |        0 | fr   | Monde    |  1 |
|      0 |  1 |        1 | de   | Welt     |  2 |
|      0 |  1 |        2 | def  | WorldDEF |  3 |
+--------+----+----------+------+----------+----+

结果

WITH
CTE
AS
(
    SELECT
        #texts.module
        ,#texts.id
        ,#langs.priority
        ,#langs.lang
        ,#texts.txt
        ,ROW_NUMBER() OVER (PARTITION BY #texts.id ORDER BY #langs.priority) AS rn
    FROM
        #texts
        INNER JOIN #langs ON #langs.lang = #texts.lang
    WHERE
        #langs.first = 'fr' --! language goes here
        AND #texts.module = 0 --! module integer goes here
)
SELECT
    module
    ,id
    ,lang
    ,txt
FROM CTE
WHERE rn = 1
ORDER BY
    id
;

最终查询

+--------+----+------+-------+
| module | id | lang |  txt  |
+--------+----+------+-------+
|      0 |  0 | de   | Hallo |
|      0 |  1 | fr   | Monde |
+--------+----+------+-------+

结果

drop table #texts;
drop table #langs;

清理

File.Copy()