Sql 将表与其自身联接,并使用位操作筛选每个表中的行

Sql 将表与其自身联接,并使用位操作筛选每个表中的行,sql,postgresql,database-design,left-join,Sql,Postgresql,Database Design,Left Join,我有下表 -- Generated with pg_dump, some constraints are missing CREATE TABLE articulos_factura_venta ( fila integer NOT NULL, cantidad integer NOT NULL, color integer NOT NULL, talla integer NOT NULL, estado integer DEFAULT 2 NOT NUL

我有下表

-- Generated with pg_dump, some constraints are missing
CREATE TABLE articulos_factura_venta (
    fila integer NOT NULL,
    cantidad integer NOT NULL,
    color integer NOT NULL,
    talla integer NOT NULL,
    estado integer DEFAULT 2 NOT NULL,
    origen integer,
    factura integer NOT NULL,
    articulo integer NOT NULL,
    precio integer NOT NULL,
    vendedor integer,
    anulado boolean DEFAULT false,
    iva double precision DEFAULT 12.0,
    fecha date DEFAULT ('now'::text)::date NOT NULL
);
并且它包含以下行1

fila | cantidad | color | talla | estado | origen | factura | articulo | precio | vendedor | anulado | iva | fecha
------+----------+-------+-------+--------+--------+---------+----------+--------+----------+---------+-----+------------
0 | 1 | 0 | 3 | 6 | 18 | 28239 | 1325 | 455 | 6 | f | 0 | 2015-04-22
1 | 1 | 0 | 2 | 6 | 93 | 28239 | 2071 | 615 | 6 | f | 0 | 2015-04-22
2 | 1 | 0 | 49 | 6 | 76 | 28239 | 2013 | 545 | 6 | f | 0 | 2015-04-22
3 | 1 | 0 | 78 | 6 | 85 | 28239 | 2042 | 235 | 6 | f | 0 | 2015-04-22
4 | 1 | 0 | 49 | 6 | 81 | 28239 | 2026 | 615 | 6 | f | 0 | 2015-04-22
5 | 1 | 0 | 50 | 6 | 90 | 28239 | 2051 | 755 | 6 | f | 0 | 2015-04-22
6 | 1 | 0 | 1 | 38 | 21 | 28239 | 1780 | 495 | 6 | f | 0 | 2015-04-22
7 | 1 | 15 | 2 | 38 | 16 | 28239 | 1323 | 845 | 6 | f | 0 | 2015-04-22
8 | 1 | 0 | 4 | 38 | 18 | 28239 | 1326 | 455 | 6 | f | 0 | 2015-04-22
2 | 1 | 0 | 49 | 22 | 76 | 28239 | 2013 | 545 | 6 | f | 0 | 2015-04-22
问题很简单,为什么这个查询不输出行

SELECT
    filas.factura,
    filas.fila,
    filas.cantidad,
    retirados.cantidad,
    vendidos.cantidad,
    filas.estado
FROM
    articulos_factura_venta AS filas
LEFT JOIN
    articulos_factura_venta AS retirados
    USING (fila, color, talla, origen, factura, articulo, vendedor)
LEFT JOIN
    articulos_factura_venta AS vendidos
    USING (fila, color, talla, origen, factura, articulo, vendedor)
JOIN
    articulos
    ON articulos.codigo = filas.articulo
JOIN
    tallas
    ON tallas.codigo = filas.talla
JOIN
    colores
    ON colores.codigo = filas.color
JOIN
    empleados
    ON empleados.codigo = filas.vendedor
WHERE
    filas.factura = 28239 AND 
    retirados.estado & 16 <> 0 AND 
    vendidos.estado & 8 <> 0 AND
    filas.estado & 4 <> 0
ORDER BY
    filas.estado


[1] 该表包含数千行,但我只对这些行感兴趣,即
factura==28239

这是一个称为链式外部联接的问题。首先执行一些
LEFT-OUTER-JOIN
,它会为右表中与左表不匹配的列创建
NULL
值。然后,当您将这些
NULL
值与之后的
内部联接
进行联接时,这些行将消失,就好像您一开始从未进行过外部联接一样

有两种解决方案:

  • 一旦开始
    左连接
    所有后续的
    连接
    必须是
    左连接
    完全连接
  • 更好的选择是首先执行所有的
    内部联接
    ,然后执行所需的表
    想成为外部最后一个
    右连接

  • 另外,在执行
    外部联接时,无论是
    还是
    ,通常最好将
    WHERE
    子句条件移动到
    ON
    子句中,而不是将
    WHERE
    子句中。这是一个非常棘手的问题,但请查找筛选条件和加入条件之间的区别,以及它们何时应放置在
    WHERE
    vs
    ON
    子句中。

    长话短说,可能是这样的:

    SELECT f.factura
         , f.fila
         , f.cantidad
         , r.cantidad
         , v.cantidad
         , f.estado
    FROM   articulos_factura_venta f
    -- JOIN   articulos a ON a.codigo = f.articulo  -- just noise
    -- JOIN   tallas    t ON t.codigo = f.talla
    -- JOIN   colores   c ON c.codigo = f.color
    JOIN   empleados e ON e.codigo = f.vendedor
    LEFT   JOIN articulos_factura_venta r ON r.fila = f.fila
                                         AND r.color = f.color
                                         AND r.talla = f.talla
                                         AND r.origen = f.origen
                                         AND r.factura = f.factura
                                         AND r.articulo = f.articulo
                                         AND r.vendedor = f.vendedor
                                         AND r.estado & 16 <> 0
    LEFT   JOIN articulos_factura_venta v ON v.fila = f.fila
                                         AND v.color = f.color
                                         AND v.talla = f.talla
                                         AND v.origen = f.origen
                                         AND v.factura = f.factura
                                         AND v.articulo = f.articulo
                                         AND v.vendedor = f.vendedor
                                         AND v.estado & 8 <> 0
    WHERE  f.factura = 28239
    AND    f.estado & 4 <> 0
    ORDER  BY f.estado;
    
    另一个棘手的细节:

    JOIN   empleados e ON e.codigo = f.vendedor
    
    但是
    f.vendedor
    可以
    NULL
    。您是否打算从结果中删除带有
    f的所有行。vendedor为NULL
    ?因为这就是join所做的

    我对
    articulos
    tallas
    colors
    的三个连接进行了注释。FK列为
    非空
    ,联接只会消耗时间,并且您不使用任何列

    表定义 超过7列的主键约束是一个糟糕的想法。昂贵而笨拙。添加代理主键-我建议使用
    serial
    列:

    如果您确实需要,您仍然可以使用
    UNIQUE
    约束在7列集合上强制唯一性。
    关于和约束(根据注释中的请求):

    建议的表格设计:

    CREATE TABLE articulos_factura_venta (
        afv_id serial PRIMARY KEY  -- pick your column name
        fila integer NOT NULL,
        cantidad integer NOT NULL,
        color integer NOT NULL,
        talla integer NOT NULL,
        estado integer DEFAULT 2 NOT NULL,
        factura integer NOT NULL,
        articulo integer NOT NULL,
        precio integer NOT NULL,
        fecha date NOT NULL DEFAULT now()::date,
        origen integer,
        vendedor integer,
        anulado boolean DEFAULT false,  -- NOT NULL ?
        iva double precision DEFAULT 12.0,
        CONSTRAINT uni7  -- pick your contraint name
         UNIQUE (fila, factura, articulo, precio, talla, color, estado)
    );
    创建表articulos\u factura\u venta(
    afv_id序列主键--选择您的列名
    一个整数不为空,
    cantidad整数不为空,
    颜色整数不为空,
    整数不为空,
    estado整数默认值2不为空,
    factura整数不为空,
    articulo整数不为空,
    precio整数不为空,
    fecha date NOT NULL默认值now()::日期,
    奥利根整数,
    vendedor整数,
    anulado布尔值默认值为false,--不为NULL?
    iva双精度默认值12.0,
    约束uni7——选择您的约束名称
    独特(fila、factura、articulo、precio、talla、color、estado)
    );
    然后可以将查询简化为:

    ...
    LEFT   JOIN articulos_factura_venta r ON r.afv_id = f.afv_id
                                         AND r.estado & 16 <> 0
    LEFT   JOIN articulos_factura_venta v ON v.afv_id = f.afv_id
                                         AND v.estado & 8 <> 0
    ...
    
    。。。
    在r.afv\U id=f.afv\U id上左关节连接
    r.estado和160
    v.afv\U id=f.afv\U id上的左关节连接
    v.estado和80
    ...
    
    缺少一些约束条件
    。。。但约束对于构建正确的查询至关重要。请给出完整的图片-您从psql中的
    \d articulos\u factura\u venta
    中得到了什么。有趣的是,这可能意味着我不能使用
    使用
    。我不确定您使用的SQL的哪个版本使用了USING子句,以及为什么将内部联接移到顶部会不起作用。但无论如何,你可以扩展ON子句,使用完整的ON x.col=y.col样式语法。看到另一个答案,海报删除了使用
    ,因为不可能在它后面添加
    r.estado&16 0
    ,我想,问题。我想现在我也明白了原因。@iharob:这意味着一行在一个联接表中有两个匹配项,这可能不应该发生……这是因为两行中都设置了一些位,
    4
    几乎在每一行中,因此,添加
    f.estado&8=0和f.estado&16=0实际上是有意义的
    
    ...
    LEFT   JOIN articulos_factura_venta r ON r.afv_id = f.afv_id
                                         AND r.estado & 16 <> 0
    LEFT   JOIN articulos_factura_venta v ON v.afv_id = f.afv_id
                                         AND v.estado & 8 <> 0
    ...