JOIN table 本身,并通过按位运算从每个 table 中过滤行
JOIN table with itself, and filter rows from each table with bitwise operation
我有以下 table,
-- 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
我希望此查询从具有 fila == 2
的行中减去 cantidad
,在 estado & 16 <> 0
的情况下,因此我希望只有一行具有 fila == 2
和 cantidad = 0
注意:位标志不是硬编码的,它们是我在用 c++ 编写的实际应用程序中使用的 enum
。
Table定义
database# \d articulos_factura_venta
Column | Type | Modifiers
----------+------------------+--------------------------------------
fila | integer | not null
cantidad | integer | not null
color | integer | not null
talla | integer | not null
estado | integer | not null default 2
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 | not null default ('now'::text)::date
Indexes:
"articulos_factura_venta_pkey" PRIMARY KEY, btree (fila, factura, articulo, precio, talla, color, estado)
"buscar_cantidad_venta_idx" btree (articulo, talla, color, origen)
Foreign-key constraints:
"cantidades_venta_articulo_fkey" FOREIGN KEY (articulo) REFERENCES articulos(codigo)
"cantidades_venta_color_fkey" FOREIGN KEY (color) REFERENCES colores(codigo) ON UPDATE CASCADE ON DELETE RESTRICT
"cantidades_venta_factura_fkey" FOREIGN KEY (factura) REFERENCES ventas(codigo)
"cantidades_venta_origen_fkey" FOREIGN KEY (origen) REFERENCES compras(codigo) ON UPDATE CASCADE ON DELETE RESTRICT
"cantidades_venta_talla_fkey" FOREIGN KEY (talla) REFERENCES tallas(codigo) ON UPDATE CASCADE ON DELETE RESTRICT
"cantidades_venta_vendedor_fkey" FOREIGN KEY (vendedor) REFERENCES empleados(codigo)
[1]table 包含数千行,但我只对这些行感兴趣,即 factura == 28239
.
这是一个称为链式外部联接的问题。一旦您首先执行了一些 LEFT OUTER JOIN
,它会为右侧 table 中与左侧 table 不匹配的列创建 NULL
值。然后,当您将这些 NULL
值与之后的 INNER JOIN
连接时,这些行就会消失,就好像您一开始就没有进行过外部连接一样。
有两种解决方法:
- 一旦您开始
LEFT JOIN
,所有后续的 JOIN
必须是 LEFT
或 FULL
- 更好的选择是先完成所有
INNER JOIN
然后再完成 table
想成为最后一个 RIGHT JOIN
此外,当您在执行 OUTER JOIN
时,无论是 LEFT
还是 RIGHT
,将 WHERE
CLAUSE 条件移动到 ON
子句而不是 WHERE
子句。这是一个非常棘手的问题,但请查看 FILTER 条件和 JOIN
条件之间的区别,以及何时应将它们放在 WHERE
与 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;
特别是这些添加的 WHERE
子句使您在各自的 table 上尝试 LEFT JOIN
无效,并使其表现得像 JOIN
:
AND r.estado & 16 <> 0
AND v.estado & 8 <> 0
另一个置顶细节:
JOIN empleados e ON e.codigo = f.vendedor
但是f.vendedor
可以是NULL
。您是否打算从结果中删除所有带有 f.vendedor IS NULL
的行?因为这就是连接的作用。
并且我评论了 articulos
、tallas
和 colores
的三个连接。 FK 列是 NOT NULL
,连接只会花费时间并且您没有使用任何列。
Table定义
超过 7 列的主键约束是一个糟糕的想法。昂贵且笨重。添加代理主键 - 我建议 serial
列:
- Auto increment SQL function
- Primary & Foreign Keys in pgAdmin
您仍然可以使用 UNIQUE
约束对 7 列的集合强制执行唯一性 - 如果 您确实需要它。
关于 UNIQUE
and PRIMARY KEY
约束(根据评论中的请求):
- How does PostgreSQL enforce the UNIQUE constraint / what type of index does it use?
- Do I need a primary key for my table, which has a UNIQUE (composite 4-columns), one of which can be NULL?
- Why can I create a table with PRIMARY KEY on a nullable column?
建议table设计:
CREATE TABLE articulos_factura_venta (
<b>afv_id serial PRIMARY KEY</b> -- 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,
<b>CONSTRAINT uni7 -- pick your contraint name
UNIQUE (fila, factura, articulo, precio, talla, color, estado)</b>
);
那么查询可以简化为:
...
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
...
我有以下 table,
-- 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
我希望此查询从具有 fila == 2
的行中减去 cantidad
,在 estado & 16 <> 0
的情况下,因此我希望只有一行具有 fila == 2
和 cantidad = 0
注意:位标志不是硬编码的,它们是我在用 c++ 编写的实际应用程序中使用的 enum
。
Table定义
database# \d articulos_factura_venta
Column | Type | Modifiers
----------+------------------+--------------------------------------
fila | integer | not null
cantidad | integer | not null
color | integer | not null
talla | integer | not null
estado | integer | not null default 2
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 | not null default ('now'::text)::date
Indexes:
"articulos_factura_venta_pkey" PRIMARY KEY, btree (fila, factura, articulo, precio, talla, color, estado)
"buscar_cantidad_venta_idx" btree (articulo, talla, color, origen)
Foreign-key constraints:
"cantidades_venta_articulo_fkey" FOREIGN KEY (articulo) REFERENCES articulos(codigo)
"cantidades_venta_color_fkey" FOREIGN KEY (color) REFERENCES colores(codigo) ON UPDATE CASCADE ON DELETE RESTRICT
"cantidades_venta_factura_fkey" FOREIGN KEY (factura) REFERENCES ventas(codigo)
"cantidades_venta_origen_fkey" FOREIGN KEY (origen) REFERENCES compras(codigo) ON UPDATE CASCADE ON DELETE RESTRICT
"cantidades_venta_talla_fkey" FOREIGN KEY (talla) REFERENCES tallas(codigo) ON UPDATE CASCADE ON DELETE RESTRICT
"cantidades_venta_vendedor_fkey" FOREIGN KEY (vendedor) REFERENCES empleados(codigo)
[1]table 包含数千行,但我只对这些行感兴趣,即 factura == 28239
.
这是一个称为链式外部联接的问题。一旦您首先执行了一些 LEFT OUTER JOIN
,它会为右侧 table 中与左侧 table 不匹配的列创建 NULL
值。然后,当您将这些 NULL
值与之后的 INNER JOIN
连接时,这些行就会消失,就好像您一开始就没有进行过外部连接一样。
有两种解决方法:
- 一旦您开始
LEFT JOIN
,所有后续的JOIN
必须是LEFT
或FULL
- 更好的选择是先完成所有
INNER JOIN
然后再完成 table 想成为最后一个RIGHT JOIN
此外,当您在执行 OUTER JOIN
时,无论是 LEFT
还是 RIGHT
,将 WHERE
CLAUSE 条件移动到 ON
子句而不是 WHERE
子句。这是一个非常棘手的问题,但请查看 FILTER 条件和 JOIN
条件之间的区别,以及何时应将它们放在 WHERE
与 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;
特别是这些添加的 WHERE
子句使您在各自的 table 上尝试 LEFT JOIN
无效,并使其表现得像 JOIN
:
AND r.estado & 16 <> 0
AND v.estado & 8 <> 0
另一个置顶细节:
JOIN empleados e ON e.codigo = f.vendedor
但是f.vendedor
可以是NULL
。您是否打算从结果中删除所有带有 f.vendedor IS NULL
的行?因为这就是连接的作用。
并且我评论了 articulos
、tallas
和 colores
的三个连接。 FK 列是 NOT NULL
,连接只会花费时间并且您没有使用任何列。
Table定义
超过 7 列的主键约束是一个糟糕的想法。昂贵且笨重。添加代理主键 - 我建议 serial
列:
- Auto increment SQL function
- Primary & Foreign Keys in pgAdmin
您仍然可以使用 UNIQUE
约束对 7 列的集合强制执行唯一性 - 如果 您确实需要它。
关于 UNIQUE
and PRIMARY KEY
约束(根据评论中的请求):
- How does PostgreSQL enforce the UNIQUE constraint / what type of index does it use?
- Do I need a primary key for my table, which has a UNIQUE (composite 4-columns), one of which can be NULL?
- Why can I create a table with PRIMARY KEY on a nullable column?
建议table设计:
CREATE TABLE articulos_factura_venta (
<b>afv_id serial PRIMARY KEY</b> -- 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,
<b>CONSTRAINT uni7 -- pick your contraint name
UNIQUE (fila, factura, articulo, precio, talla, color, estado)</b>
);
那么查询可以简化为:
...
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
...