将INTERSECT与WITH子句中的表一起使用

bla*_*aze 2 sql postgresql common-table-expression intersect relational-division

这个查询有什么问题:

WITH volcan AS (SELECT DISTINCT v.numturista
                FROM viaje v, sitio s
                WHERE v.numsitio = s.numsitio
                AND s.tipo = 'Volcan'),
    desierto AS (SELECT DISTINCT v.numturista
                 FROM viaje v, sitio s
                 WHERE v.numsitio = s.numsitio
                 AND s.tipo = 'Desierto')
SELECT DISTINCT pais
FROM turista
WHERE numturista IN (volcan INTERSECT desierto);
Run Code Online (Sandbox Code Playgroud)

它不应该等同于以下(因为WITH创建命名的SELECT查询):

SELECT DISTINCT pais
FROM turista
WHERE numturista IN (
(SELECT DISTINCT v.numturista
                FROM viaje v, sitio s
                WHERE v.numsitio = s.numsitio
                AND s.tipo = 'Volcan')
INTERSECT
(SELECT DISTINCT v.numturista
                 FROM viaje v, sitio s
                 WHERE v.numsitio = s.numsitio
                 AND s.tipo = 'Desierto')
);
Run Code Online (Sandbox Code Playgroud)

它说:

syntax error near INTERSECT.
Run Code Online (Sandbox Code Playgroud)

Postgres版本:psql(PostgreSQL)9.1.9

更新:测试表

CREATE TABLE turista (
    numturista INTEGER,
    nomturista VARCHAR(100),
    pais VARCHAR(100),
    PRIMARY KEY(numturista)
);

CREATE TABLE sitio (
    numsitio INTEGER,
    nomsitio VARCHAR(100),
    tipo VARCHAR(100),
    continente VARCHAR(100),
    PRIMARY KEY(numsitio)
);

CREATE TABLE viaje (
    numviaje VARCHAR(7),
    numturista INTEGER,
    numsitio INTEGER, 
    fechasalida DATE,
    fechallegada DATE,
    ciudadsalida VARCHAR(100),
    PRIMARY KEY(numviaje, numturista, numsitio),
    FOREIGN KEY(numsitio) REFERENCES sitio,
    FOREIGN KEY(numturista) REFERENCES turista
);

INSERT INTO turista VALUES
 (300, 'Carlos', 'Costa Rica')
,(301, 'Pierre', 'Francia')
,(302, 'John', 'Jamaica')
,(303, 'Mario', 'Panama')
,(304, 'Ali', 'Tunez')
,(305, 'Ana', 'Guatemala');

INSERT INTO sitio VALUES 
 (125, 'Isla Moorea', 'Mar Litoral', 'Oceania')
,(126, 'Bahia Matsushima', 'Mar Litoral', 'Asia')
,(127, 'Irazu', 'Volcan', 'America')
,(128, 'Ngorongoro', 'Volcan', 'Africa')
,(129, 'Valle de la Muerte', 'Desierto', 'America')
,(130, 'Kilimandjar', 'Volcan', 'Africa');

INSERT INTO viaje VALUES
 ('03-2012', 301, 125, '2013-03-03', '2013-10-03', 'Paris')
,('04-2012', 303, 129, '2013-04-07', '2014-02-07', 'Las Vegas')
,('05-2012', 301, 128, '2013-05-07', '2013-12-07', 'Dar-es-Salam')
,('06-2012', 304, 127, '2013-06-07', '2014-02-07', 'San Jose')
,('07-2012', 302, 128, '2015-04-11', '2014-01-08', 'Mombasa')
,('04-2012', 305, 129, '2013-04-07', '2014-02-07', 'Las Vegas')
,('06-2012', 305, 127, '2013-06-07', '2014-02-07', 'San Jose');
Run Code Online (Sandbox Code Playgroud)

Erw*_*ter 5

这相当于:

WITH volcan AS (
    SELECT DISTINCT v.numturista
    FROM   viaje v
    JOIN   sitio s USING (numsitio)
    WHERE  s.tipo = 'Volcan'
   )
, desierto AS (
    SELECT DISTINCT v.numturista
    FROM   viaje v
    JOIN   sitio s USING (numsitio)
    WHERE  s.tipo = 'Desierto'
   )
SELECT DISTINCT pais
FROM   turista
WHERE  numturista IN ((TABLE volcan) INTERSECT (TABLE desierto));
Run Code Online (Sandbox Code Playgroud)

但它可能效率很低......

TABLE tbl只是一个符号的捷径SELECT * FROM tbl.

替代查询

试试这个:

SELECT pais
FROM   turista t
WHERE  EXISTS (SELECT 1 FROM viaje v JOIN sitio s USING (numsitio)
               WHERE  v.numturista = t.numturista AND s.tipo = 'Volcan')
AND    EXISTS (SELECT 1 FROM viaje v JOIN sitio s USING (numsitio)
               WHERE  v.numturista = t.numturista AND s.tipo = 'Desierto')
Run Code Online (Sandbox Code Playgroud)

是一样的,只是更简单,更快捷.有了EXISTS,你不需要任何DISTINCT条款,除非你真的有重复的国家(pais),我很怀疑.