在PostgreSQL中将值从一行复制到另一行
我有一张这样的桌子:在PostgreSQL中将值从一行复制到另一行,postgresql,Postgresql,我有一张这样的桌子: id product amount 1 A 6 1 A 8 1 A 1 B 1 1 B 2 C 2 2 C 2 C 4 2 C 2 C id product amount 1 A 6 1 A 8 1 A
id product amount
1 A 6
1 A 8
1 A
1 B 1
1 B
2 C 2
2 C
2 C 4
2 C
2 C
id product amount
1 A 6
1 A 8
1 A 8
1 B 1
1 B 1
2 C 2
2 C 2
2 C 4
2 C 4
2 C 4
我需要这样做:
id product amount
1 A 6
1 A 8
1 A
1 B 1
1 B
2 C 2
2 C
2 C 4
2 C
2 C
id product amount
1 A 6
1 A 8
1 A 8
1 B 1
1 B 1
2 C 2
2 C 2
2 C 4
2 C 4
2 C 4
按以前的非缺失值复制金额
我尝试使用lag()
函数。但是,更新中不允许使用聚合函数lag()
update tableA set amount = lag(amount);
使用PostgreSQL我能做什么?在不知道“前几行”的定义的情况下,一切都是猜测。但您可以使用a来做您想做的事情,只需进行更改:
CREATE TEMPORARY TABLE test_lag AS
SELECT column1 AS id, column2 AS product, column3 AS amount FROM (
VALUES (1, 'A', 6),
(1, 'A', 8),
(1, 'A', NULL),
(1, 'B', 1),
(1, 'B', NULL),
(2, 'C', 2),
(2, 'C', NULL),
(2, 'C', 4),
(2, 'C', NULL),
(2, 'C', NULL)) AS tmp;
DO $$
BEGIN
--Loop until update all null amounts
--Why we need this? It's because PostgreSQL don't supports IGNORE NULLS clause on lag()
LOOP
WITH tmp AS (
SELECT ctid, lag(amount) OVER() AS last_amount FROM test_lag ORDER BY id, product -- You MUST change this ORDER to right columns (What's previous row?)
)
UPDATE test_lag SET amount = tmp.last_amount FROM tmp WHERE test_lag.ctid = tmp.ctid AND amount IS NULL;
IF NOT FOUND THEN
EXIT;
END IF;
END LOOP;
END $$;
SELECT * FROM test_lag ORDER BY id, product, amount;
如果不知道“前几行”的定义,一切都只是猜测。但您可以使用a来做您想做的事情,只需进行更改:
CREATE TEMPORARY TABLE test_lag AS
SELECT column1 AS id, column2 AS product, column3 AS amount FROM (
VALUES (1, 'A', 6),
(1, 'A', 8),
(1, 'A', NULL),
(1, 'B', 1),
(1, 'B', NULL),
(2, 'C', 2),
(2, 'C', NULL),
(2, 'C', 4),
(2, 'C', NULL),
(2, 'C', NULL)) AS tmp;
DO $$
BEGIN
--Loop until update all null amounts
--Why we need this? It's because PostgreSQL don't supports IGNORE NULLS clause on lag()
LOOP
WITH tmp AS (
SELECT ctid, lag(amount) OVER() AS last_amount FROM test_lag ORDER BY id, product -- You MUST change this ORDER to right columns (What's previous row?)
)
UPDATE test_lag SET amount = tmp.last_amount FROM tmp WHERE test_lag.ctid = tmp.ctid AND amount IS NULL;
IF NOT FOUND THEN
EXIT;
END IF;
END LOOP;
END $$;
SELECT * FROM test_lag ORDER BY id, product, amount;
您可以
选择您想要更新的内容,但是没有(简单的)方法来实际执行更新,因为表fox还没有主键
用一些数据填充狐狸
INSERT INTO fox VALUES
(1, 'A', 6),
(1, 'A', 8),
(1, 'A', NULL),
(1, 'B', 1),
(1, 'B', NULL),
(2, 'C', 2),
(2, 'C', NULL),
(2, 'C', 4),
(2, 'C', NULL),
(2, 'C', NULL),
(3, 'What does the fox say?', 5);
查询
WITH ranks (rank, id, product, amount) AS (
SELECT ROW_NUMBER() OVER (), id, product, amount FROM foo
)
SELECT r.id, r.product,
(SELECT amount FROM ranks
WHERE id = r.id AND product = r.product
AND rank < r.rank AND amount IS NOT NULL
ORDER BY amount DESC LIMIT 1
)
FROM ranks r WHERE r.amount IS NULL ORDER BY 1, 2, 3;
但是您不能使用此数据进行更新,因为行仍然不是由(id,product)
唯一标识的-这意味着您无法编写一个,其中
条件唯一标识您的行。WHERE
子句如何知道在更新中是将金额更改为2还是4?(id,product)=(2,'C')
的多行在更新的中的中无法区分
BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE ;
WITH nulls AS (
SELECT pkey, id, product
FROM fox
WHERE amount IS NULL
), changes AS (
SELECT pkey,
(SELECT amount FROM fox
WHERE id = n.id AND product = n.product
AND n.pkey > pkey AND amount IS NOT NULL
ORDER BY pkey DESC LIMIT 1)
FROM nulls n
) UPDATE fox f SET amount = c.amount FROM changes c WHERE f.pkey = c.pkey ;
让我们给狐狸一个主键
ALTER TABLE fox ADD COLUMN IF NOT EXISTS pkey serial ;
ALTER TABLE fox ADD PRIMARY KEY (pkey) ;
现在我们可以通过主键pkey
来识别行
WITH nulls AS (
SELECT pkey, id, product
FROM fox
WHERE amount IS NULL
)
SELECT pkey,
id, product, -- you can leave these out in your UPDATE: pkey is UNIQUE
(SELECT amount FROM fox
WHERE id = n.id AND product = n.product
AND n.pkey > pkey AND amount IS NOT NULL
ORDER BY pkey DESC LIMIT 1)
FROM nulls n ORDER BY 1, 2, 3, 4;
显示要进行的更改
pkey | id | product | amount
------+----+---------+--------
3 | 1 | A | 8
5 | 1 | B | 1
7 | 2 | C | 2
9 | 2 | C | 4
10 | 2 | C | 4
我们可以在UPDATE
中使用pkey
BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE ;
WITH nulls AS (
SELECT pkey, id, product
FROM fox
WHERE amount IS NULL
), changes AS (
SELECT pkey,
(SELECT amount FROM fox
WHERE id = n.id AND product = n.product
AND n.pkey > pkey AND amount IS NOT NULL
ORDER BY pkey DESC LIMIT 1)
FROM nulls n
) UPDATE fox f SET amount = c.amount FROM changes c WHERE f.pkey = c.pkey ;
检查结果是否正常:
SELECT * FROM fox ORDER BY 1, 2, 3, 4;
并相应地使用COMMIT
或ROLLBACK
接受
添加主键的备选方案
每个表都应该始终有一个主键。
如果您坚持不拥有一个,那么您也可以使用其当时的非空金额计算行,并而不是更新它们,您可以将它们插入表中并然后从福克斯删除金额为空的行
删除没有金额的行。这样您就可以添加一个唯一的主键。当然,UPDATE
和DELETE
被打包成一个事务
,这样就不会干扰同时运行的其他事务。例如,在使用SELECT
计算要插入的数据之后,以及在DELETE
之前,另一笔交易添加了NULL
金额的行。在这种情况下,您将错过同时添加的NULL amount
行(并发导致的数据丢失;请考虑ACID)。
但是丢失的主键以后可能会咬到你。你可以选择你想要更新的内容,但是没有(简单的)方法来实际执行更新,因为fox表还没有主键
用一些数据填充狐狸
INSERT INTO fox VALUES
(1, 'A', 6),
(1, 'A', 8),
(1, 'A', NULL),
(1, 'B', 1),
(1, 'B', NULL),
(2, 'C', 2),
(2, 'C', NULL),
(2, 'C', 4),
(2, 'C', NULL),
(2, 'C', NULL),
(3, 'What does the fox say?', 5);
查询
WITH ranks (rank, id, product, amount) AS (
SELECT ROW_NUMBER() OVER (), id, product, amount FROM foo
)
SELECT r.id, r.product,
(SELECT amount FROM ranks
WHERE id = r.id AND product = r.product
AND rank < r.rank AND amount IS NOT NULL
ORDER BY amount DESC LIMIT 1
)
FROM ranks r WHERE r.amount IS NULL ORDER BY 1, 2, 3;
但是您不能使用此数据进行更新,因为行仍然不是由(id,product)
唯一标识的-这意味着您无法编写一个,其中
条件唯一标识您的行。WHERE
子句如何知道在更新中是将金额更改为2还是4?(id,product)=(2,'C')
的多行在更新的中的中无法区分
BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE ;
WITH nulls AS (
SELECT pkey, id, product
FROM fox
WHERE amount IS NULL
), changes AS (
SELECT pkey,
(SELECT amount FROM fox
WHERE id = n.id AND product = n.product
AND n.pkey > pkey AND amount IS NOT NULL
ORDER BY pkey DESC LIMIT 1)
FROM nulls n
) UPDATE fox f SET amount = c.amount FROM changes c WHERE f.pkey = c.pkey ;
让我们给狐狸一个主键
ALTER TABLE fox ADD COLUMN IF NOT EXISTS pkey serial ;
ALTER TABLE fox ADD PRIMARY KEY (pkey) ;
现在我们可以通过主键pkey
来识别行
WITH nulls AS (
SELECT pkey, id, product
FROM fox
WHERE amount IS NULL
)
SELECT pkey,
id, product, -- you can leave these out in your UPDATE: pkey is UNIQUE
(SELECT amount FROM fox
WHERE id = n.id AND product = n.product
AND n.pkey > pkey AND amount IS NOT NULL
ORDER BY pkey DESC LIMIT 1)
FROM nulls n ORDER BY 1, 2, 3, 4;
显示要进行的更改
pkey | id | product | amount
------+----+---------+--------
3 | 1 | A | 8
5 | 1 | B | 1
7 | 2 | C | 2
9 | 2 | C | 4
10 | 2 | C | 4
我们可以在UPDATE
中使用pkey
BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE ;
WITH nulls AS (
SELECT pkey, id, product
FROM fox
WHERE amount IS NULL
), changes AS (
SELECT pkey,
(SELECT amount FROM fox
WHERE id = n.id AND product = n.product
AND n.pkey > pkey AND amount IS NOT NULL
ORDER BY pkey DESC LIMIT 1)
FROM nulls n
) UPDATE fox f SET amount = c.amount FROM changes c WHERE f.pkey = c.pkey ;
检查结果是否正常:
SELECT * FROM fox ORDER BY 1, 2, 3, 4;
并相应地使用COMMIT
或ROLLBACK
接受
添加主键的备选方案
每个表都应该始终有一个主键。
如果您坚持不拥有一个,那么您也可以使用其当时的非空金额计算行,并而不是更新它们,您可以将它们插入表中并然后从福克斯删除金额为空的行
删除没有金额的行。这样您就可以添加一个唯一的主键。当然,UPDATE
和DELETE
被打包成一个事务
,这样就不会干扰同时运行的其他事务。例如,在使用SELECT
计算要插入的数据之后,以及在DELETE
之前,另一笔交易添加了NULL
金额的行。在这种情况下,您将错过同时添加的NULL amount
行(并发导致的数据丢失;请考虑ACID)。
但是丢失的主键以后可能会咬到你。这里“上一行”的定义是什么?按id、产品、随机()或ctid排序是的,类似于lag()函数。但是,我不能在更新中使用聚合函数lag()。您可以在CTE中使用lag()-问题是缺少排序列什么是CTE?我想我不能按id和product排序,因为id=2,product=C对应两个值:2和4这里“previous”行的定义是什么?按id、product、random()或ctid排序我相信是的,类似于lag()函数。但是,我不能在更新中使用聚合函数lag()。您可以在CTE中使用lag()-问题是缺少排序列什么是CTE?我想我不能按id和product排序,因为id=2,product=C对应两个值:2和4函数只会用相邻的值填充NULL
。存在多个NULL
值。因此,您的查询需要运行n
次,以填充一个值x
后跟n
NULL
值的序列。其他