如何转换 SQL 语句“从没有 someID 的 TABLE 中删除(从 Table group by property1、property2 中选择 someID)
Posted
技术标签:
【中文标题】如何转换 SQL 语句“从没有 someID 的 TABLE 中删除(从 Table group by property1、property2 中选择 someID)【英文标题】:How to convert SQL statement "delete from TABLE where someID not in (select someID from Table group by property1, property2) 【发布时间】:2015-07-21 15:19:49 【问题描述】:我正在尝试将以下 SQL 语句转换为 Core Data:
delete from SomeTable
where someID not in (
select someID
from SomeTable
group by property1, property2, property3
)
基本上,如果 property1、property2 和 property3 等于另一条记录,我想检索和删除表中可能的重复记录。
我该怎么做?
PS:正如标题所说,我是在尝试将上面的 SQL 语句转换成 ios Core Data 的方法,而不是尝试对上面的 SQL 进行改进、更正或评论,那是多余的。
谢谢。
【问题讨论】:
您的起始查询似乎没有按照您描述的那样做。您是要求更好的 SQL 查询,还是要求特定于 Core Data 的东西? 另外,我们是否理解SomeTable.someId
列是唯一的或(更好)PK?
嘿约翰。好吧,我在一个 iOS 项目中使用原始 SQL 选择了这个项目。我不想开始深入研究,所以当我重写应用程序时,我选择了 Core Data 作为后端。现在,我无法判断此方法是否返回它应该返回的内容,但这是用于删除具有相同 3 个属性值的记录重复项的方法,例如属性 1、属性 2 和属性 3。我正在研究如何将其转换为核心数据。哦,还有 SomeTable.someID 是独一无二的。
mysql 是我所知道的唯一一个完全接受该声明的 DBMS。在这种情况下,结果将是从每组欺骗中删除除一个之外的所有内容。 MySQL 没有定义将在每个组中保留哪一行。然而,我不是这样理解你的要求的。我会尽快更新我的答案。
【参考方案1】:
听起来您要求使用 SQL 来实现您的目标。您的起始查询不会按照您的描述进行,并且大多数数据库根本不会接受它,因为聚合子查询试图选择不是组函数的列。
更新
我最初认为请求是删除每个包含欺骗的组的所有成员,并相应地编写了代码。像 MySQL 一样重新解释了原始 SQL,似乎目标是为(property1, property2, property3)
的每个组合保留一个元素。我想无论如何这更有意义。这是执行此操作的标准方法:
delete from SomeTable st1
where someID not in (
select min(st2.someId)
from SomeTable st2
group by property1, property2, property3
)
这与原来的区别在于使用min()
聚合函数从每个组中选择要保留的someId
值中的一个特定值。这也应该有效:
delete from SomeTable st1
where someID in (
select st3.someId
from SomeTable st2
join SomeTable st3
on st2.property1 = st3.property1
and st2.property2 = st3.property2
and st2.property3 = st3.property3
where st2.someId < st3.someId
)
这两个查询将保留相同的行。我更喜欢第二个,即使它更长,因为NOT IN
运算符对于从大集合中选择少量元素来说有点讨厌。但是,如果您预计有足够多的行来关注缩放,那么您应该同时尝试这两种方法,并可能考虑优化(例如,(property1, property2, property3)
上的索引)和其他替代方案。
然而,至于根据 Core Data 调用来编写它,我认为您并不完全可以。 Core Data 确实支持分组,因此您可以编写 Core Data 调用,在第一个备选方案中执行子查询并返回实体对象或其 ID,按描述分组。然后,您可以遍历组,跳过每个组的第一个元素,并为所有其余部分调用 Core Data 删除方法。详细信息超出了 SO 格式的范围。
不过,我不得不说,在 Core Data 中做这样的工作将比直接在数据库中做更远的成本,无论是在时间上还是在所需的内存上。然而,直接在数据库中执行它对诸如 Core Data 之类的 ORM 框架并不友好。这种事情是您通过使用 ORM 框架选择的权衡之一。
我建议您尽量避免这样做。在SomeTable(property1, property2, property3)
上定义一个唯一索引,并尽您所能避免尝试创建重复项或从(失败的)尝试中正常恢复。
【讨论】:
显然有些人在 SQL 方面比我好很多!我如何将其转换为正确的核心数据调用? 感谢您抽出宝贵时间回复,约翰。我显然不能直接从数据库中删除行。这显然会搞砸 Core Data。我将看看如何使用您的逻辑来提出 Core Data 可接受的内容。【参考方案2】:DELETE SomeTable
FROM SomeTable
LEFT OUTER JOIN (
SELECT MIN(RowId) as RowId, property1, property2, property3
FROM SomeTable
GROUP BY property1, property2, property3
) as KeepRows ON
SomeTable.RowId = KeepRows.RowId
WHERE
KeepRows.RowId IS NULL
【讨论】:
【参考方案3】:在 iOS 中执行此操作的几点建议: 在 iOS 9 之前,删除对象的唯一方法是单独删除,即您需要遍历一组重复项并删除每个重复项。 (如果您的目标是 iOS9,有一个新的 NSBatchDeleteRequest
将有助于一次性删除它们 - 它确实直接作用于商店,但也会进行一些清理以确保在必要时更新关系)。
另一个问题是识别重复项。您可以配置提取以对其结果进行分组(参见NSFetchRequest
的propertiesToGroupBy
),但您必须指定NSDictionaryResultType
(因此结果不是对象本身,只是相关属性中的值。)此外,CoreData 将不允许您获取未在 GROUP BY 中指定的属性(聚合除外)。因此,使用min(someId)
的建议(在另一个答案中)将是必要的。 (要获取这样的表达式,您需要使用NSExpression
,将其嵌入到NSExpressionDescription
中,并将后者传递到获取请求的propertiesToFetch
中)。
最终结果将是一个字典数组,每个字典都包含您的主要记录(即您不想删除的那些)的someId
值,然后您必须从中计算出重复项。方法有很多种,但没有一种是非常有效的。
因此,正如另一个答案所说,首先最好避免重复。在这方面,请注意 iOS 9 允许您指定您希望是唯一的属性(单独或集体)。
如果您希望我详细说明上述任何内容,请告诉我。
【讨论】:
谢谢,我会试一试。我知道 NSExpressions,尽管我从未使用过它们。【参考方案4】:Group-wise Maximum:
select t1.someId
from SomeTable t1
left outer join SomeTable t2
on t1.property1 = t2.property1
and t1.property2 = t2.property2
and t1.property3 = t2.property3
and t1.someId < t2.someId
where t2.someId is null;
所以,这可能是答案
delete SomeTable
where someId not in
(select t1.someId
from SomeTable t1
left outer join SomeTable t2
on t1.property1 = t2.property1
and t1.property2 = t2.property2
and t1.property3 = t2.property3
and t1.someId < t2.someId
where t2.someId is null);
Sqlfiddle demo
【讨论】:
【参考方案5】:您可以使用exists 函数检查每一行是否存在另一行,其 id 不等于当前行,并且定义每行重复条件的所有其他属性都等于当前行。
delete from something
where
id in (SELECT
sm.id
FROM
sometable sm
where
exists( select
1
from
sometable sm2
where
sm.prop1 = sm2.prop1
and sm.prop2 = sm2.prop2
and sm.prop3 = sm2.prop3
and sm.id != sm2.id)
);
【讨论】:
【参考方案6】:我认为您可以通过创建派生的duplicate_flg
列轻松处理此问题,并在所有三个属性值相等时将其设置为 1。完成后,您可以删除duplicate_flg
= 1 的那些记录。以下是有关如何执行此操作的示例查询:
--retrieve all records that has same property values (property1,property2 and property3)
SELECT *
FROM (
SELECT someid
,property1
,property2
,property3
,CASE
WHEN property1 = property2
AND property1 = property3
THEN 1
ELSE 0
END AS duplicate_flg
FROM SomeTable
) q1
WHERE q1.duplicate_flg = 1;
这是一个示例delete
声明:
DELETE
FROM something
WHERE someid IN (
SELECT someid
FROM (
SELECT someid
,property1
,property2
,property3
,CASE
WHEN property1 = property2
AND property1 = property3
THEN 1
ELSE 0
END AS duplicate_flg
FROM SomeTable
) q1
WHERE q1.duplicate_flg = 1
);
【讨论】:
【参考方案7】:简单地说,如果你想从表中删除重复,你可以在下面执行查询:
从 SomeTable 中删除 其中rowid不在( 选择最大值(rowid) 来自 SomeTable 按属性 1、属性 2、属性 3 分组 )
【讨论】:
【参考方案8】:如果你想删除所有重复记录试试下面的代码
WITH tblTemp as
(
SELECT ROW_NUMBER() Over(PARTITION BY Property1,Property2,Property3 ORDER BY Property1) As RowNumber,* FROM Table_1
)
DELETE FROM tblTemp where RowNumber >1
希望对你有帮助
【讨论】:
【参考方案9】:使用以下查询从该表中删除重复数据
从 SomeTable 中删除 someID 不在的位置 (从 SomeTable 中选择 Min(someID) 按属性1+属性2+属性3)分组
【讨论】:
以上是关于如何转换 SQL 语句“从没有 someID 的 TABLE 中删除(从 Table group by property1、property2 中选择 someID)的主要内容,如果未能解决你的问题,请参考以下文章