she*_*ats 382 sql postgresql distinct duplicates sql-update
我需要检索表中的所有行,其中2列组合都是不同的.因此,我希望所有在同一天没有任何其他销售的销售以相同的价格出售.基于日期和价格的唯一销售将更新为活动状态.
所以我在想:
UPDATE sales
SET status = 'ACTIVE'
WHERE id IN (SELECT DISTINCT (saleprice, saledate), id, count(id)
FROM sales
HAVING count = 1)
Run Code Online (Sandbox Code Playgroud)
但是我的大脑比那更远了.
Joe*_*orn 416
SELECT DISTINCT a,b,c FROM t
Run Code Online (Sandbox Code Playgroud)
是大致等效于:
SELECT a,b,c FROM t GROUP BY a,b,c
Run Code Online (Sandbox Code Playgroud)
习惯GROUP BY语法是个好主意,因为它更强大.
对于您的查询,我会这样做:
UPDATE sales
SET status='ACTIVE'
WHERE id IN
(
SELECT id
FROM sales S
INNER JOIN
(
SELECT saleprice, saledate
FROM sales
GROUP BY saleprice, saledate
HAVING COUNT(*) = 1
) T
ON S.saleprice=T.saleprice AND s.saledate=T.saledate
)
Run Code Online (Sandbox Code Playgroud)
Erw*_*ter 331
如果你把目前为止的答案放在一起,清理和改进,你会得到这个优越的问题:
UPDATE sales
SET status = 'ACTIVE'
WHERE (saleprice, saledate) IN (
SELECT saleprice, saledate
FROM sales
GROUP BY saleprice, saledate
HAVING count(*) = 1
);
Run Code Online (Sandbox Code Playgroud)
这是很多比任何人更快.以10-15的因子(在我对PostgreSQL 8.4和9.1的测试中)来说明当前接受的答案的性能.
但这仍远未达到最佳状态.使用NOT EXISTS(反)半连接可获得更好的性能.EXISTS是标准的SQL,已经永远存在(至少自PostgreSQL 7.2以来,很久才提出这个问题)并完全符合所提出的要求:
UPDATE sales s
SET status = 'ACTIVE'
WHERE NOT EXISTS (
SELECT FROM sales s1 -- SELECT list can be empty for EXISTS
WHERE s.saleprice = s1.saleprice
AND s.saledate = s1.saledate
AND s.id <> s1.id -- except for row itself
)
AND s.status IS DISTINCT FROM 'ACTIVE'; -- avoid empty updates. see below
Run Code Online (Sandbox Code Playgroud)
如果您没有表的主键或唯一键(id在示例中),则可以使用系统列替换ctid此查询(但不是出于其他目的):
AND s1.ctid <> s.ctid
Run Code Online (Sandbox Code Playgroud)
每个表都应该有一个主键.如果还没有,请添加一个.我推荐Postgres 10+中的一个serial或一个IDENTITY专栏.
有关:
EXISTS反半连接中的子查询可以在找到第一个欺骗时立即停止评估(没有必要进一步查看).对于具有少量重复的基表,这只是稍微更有效.随着大量重复的这成为方式更有效.
如果已有一些或多行status = 'ACTIVE',您的更新将不会更改任何内容,但仍会以全部成本插入新行版本(适用次要例外).通常,你不想要这个.添加WHERE上面演示的另一个条件,使其更快:
如果status已定义NOT NULL,则可以简化为:
AND status <> 'ACTIVE';
Run Code Online (Sandbox Code Playgroud)
此查询(与Joel当前接受的答案不同)不会将NULL值视为相等.这两行将(saleprice, saledate)符合"不同"(虽然看起来与人眼相同):
(123, NULL)
(123, NULL)
Run Code Online (Sandbox Code Playgroud)
还传入一个唯一的索引,几乎在任何其他地方,因为根据SQL标准,NULL值不能相等.看到:
OTOH,GROUP BY或DISTINCT或DISTINCT ON ()将NULL值视为相等.根据您要实现的目标使用适当的查询样式.您仍然可以使用这种更快的查询样式,IS NOT DISTINCT FROM而不是使用=任何或所有比较来使NULL比较相等.更多:
如果定义了所有比较的列NOT NULL,则没有分歧的余地.
Chr*_*erg 23
您的查询的问题是,当使用GROUP BY子句(您实际上通过使用distinct)时,您只能使用分组的列或聚合函数.您不能使用列ID,因为可能存在不同的值.在你的情况下,由于HAVING子句,总是只有一个值,但大多数RDBMS都不够聪明,无法识别它.
这应该工作(并且不需要连接):
UPDATE sales
SET status='ACTIVE'
WHERE id IN (
SELECT MIN(id) FROM sales
GROUP BY saleprice, saledate
HAVING COUNT(id) = 1
)
Run Code Online (Sandbox Code Playgroud)
您也可以使用MAX或AVG而不是MIN,如果只有一个匹配的行,则使用返回列值的函数非常重要.
如果您的 DBMS 不支持多列的不同,如下所示:
select distinct(col1, col2) from table
Run Code Online (Sandbox Code Playgroud)
一般来说,多选可以安全地执行,如下所示:
select distinct * from (select col1, col2 from table ) as x
Run Code Online (Sandbox Code Playgroud)
因为这可以在大多数 DBMS 上工作,并且由于您避免了分组功能,因此预计比分组解决方案更快。
| 归档时间: |
|
| 查看次数: |
860080 次 |
| 最近记录: |