【问题标题】:How to handle this type of Oracle SQL issue如何处理此类 Oracle SQL 问题
【发布时间】:2017-07-30 21:22:34
【问题描述】:

只是有一个关于编写 SQL 的问题。
在 ORACLE DB 中,我在一个“APPLE”表中有一排不同的苹果,其中“TAGS”包含此类苹果的所有特征。例如:

NAME, TAGS 
-----------
APPLE1, FUJI BOXED MEDIUM CALIFORNIA ...  
APPLE2, ORGANIC GALA PER_POUND LARGE FLORIDA ... 
APPLE3, RED_DELICIOUS MEDIA PACKED ORGANIC ... 
APPLE4, LARGE RED_DELICIOUS Mexico .... 
APPLE5, PACKED FUJI MEXICO LARGE 

现在我想要一个 SQL 查询来找出具有任何给定标记值的所有行,例如“FUJI MEDIUM MEXICO”。这个 SQL 会是什么样子?

这与我正在进行的一个项目有关。在 DB 中,我之所以有一个“标签”列来保留所有特征,而不是单独的列,是因为我们知道将引入越来越多的新标签值,所以我们不会添加越来越多的列,而是喜欢将它们放在一列中,这样代码就不需要每次都更改。

谢谢,

杰克

【问题讨论】:

  • “我打破了第一个范式,现在查询我的数据库真的很难。”不开玩笑?看,许多数据库设计原则在一天结束时都非常灵活。通过创建多值字段来打破第一范式不是其中之一。它变得非常丑陋并且非常慢非常快。是的,像@JeffUK 这样更好的设计意味着您必须编写不会创建带有子查询等重复行的查询,但是 SQL 和数据库引擎都非常擅长这种类型的查询!他们都非常不擅长将一个字段分解为多个字段!
  • 我应该打印该评论并将其粘贴在我工作的开发部门:)
  • 请:在您提出问题之前,请在 Google 上搜索您问题的许多简洁、清晰、具体的版本。如果你问,用一个作为标题。将您的标题中的单词移动到标签中,如果它们是多余的,则将它们从您的标题中删除。这个标题对任何人都没有用。 PS 一个正确的设计没有单独的标签列,它有一个标签列,它的行说“事物名称被标记为标签”。阅读信息建模和数据库设计的一些介绍。
  • 进一步@BaconBits 点,有效标签表将允许您建立参照完整性,目前您容易受到排版错误的影响;和使用不同风格的人。例如。 RED_DELICIOUS vs RedDelicious,Spain vs Espana vs España 等等。

标签: sql oracle


【解决方案1】:

您可以重新设计表格,使其看起来像这样:

name  | tag
----------
Apple1| FUJI
Apple1| BOXED
...
Apple5| PACKED
Apple5| FUJI

然后要查找带有标签fujimediummexico 的所有项目,您可以这样做:

SELECT name from tags where tag in ('FUJI','MEDIUM','MEXICO')
GROUP BY name

您可以找到所有带有标签fujimediummexico 的项目:

SELECT name from tags where tag in ('FUJI','MEDIUM','MEXICO')
GROUP BY name
HAVING count(tag) = 3

(假设 (name,tag) 是唯一的)

这适用于任意数量的标签。还可以更轻松地从项目中删除标签,并允许您加入标签并对其进行排序。

【讨论】:

  • 但这也会返回标签值为“FUJI”、“MEDIUM”或“MEXICO”的苹果,我只需要组合这些苹果。
  • select name from tags where tag = 'FUJI' and exists (select 1 from tags t1 where t1.name = tags.name and t1.tag = 'MEDIUM') and exists (select 1 from tags t2 where t2.name = tags.name and t2.tag = 'MEXICO') 或者懒惰的,select name from (select name, count(1) as tagcount from tags where tag in ('FUJI','MEDIUM','MEXICO') group by name) where tagcount = 3
  • 您也可以使用 LISTAGG 轻松将此表格设计转换为您的原始布局,但其他方式很难转换。
  • "任何给定的标签值"对我来说意味着“或”,“所有给定的标签值”意味着 AND.. 更新了两个选项的答案
【解决方案2】:

我假设“FUJI MEDIUM MEXICO”是指您要选择带有“FUJI”“MEDIUM”“MEXICO”标签的苹果,以任何顺序。在这种情况下,以下查询将起作用:

Select name From apple
Where tag like '%FUJI%' 
  And tag like '%MEDIUM%' 
  And tag like '%MEXICO%';

正如其他人所提到的,如果您想要不区分大小写的搜索,那么您需要添加适当的 Upper 或 Lower 函数,如下所示:

Select name From apple
Where Upper(tag) like '%FUJI%' 
  And Upper(tag) like '%MEDIUM%' 
  And Upper(tag) like '%MEXICO%';

为了效率,标签应该完全大写或完全小写。这样就不需要对每一行的标签值调用 Upper() 或 Lower() 函数,如果数据集非常大,可以节省大量时间。

【讨论】:

  • 感谢您的意见,但这并不是我想要的。我试图获取查询中的标签是子集部分的所有行。好像是:“ select * from Apple where 是 apple.tags 的一个子集;”。像这样的东西,但我不知道在 SQL 查询中是否可能。
  • @user3595231 这虽然真的不清楚,但不是你如何描述你想要的问题。请编辑您的问题以澄清。请不要在 cmets 中进行澄清。
  • @user3595231 看来你想要的是一个简单的子串匹配;如果是这样,那么 Jacobm001 的答案就是您真正想要的:使用 LIKE 进行子字符串搜索。
  • @Kirby 听起来确实是这样,但我想不出你想匹配“FUJI MEDIUM MEXICO”但不匹配“FUJI MEXICO MEDIUM”的情况
【解决方案3】:

更好的设计将成为您的朋友。

三个表:

CREATE TABLE APPLE_TYPE
  (APPLE_TYPE    VARCHAR2(100));

CREATE TABLE APPLE_ATTRIBUTES
  (ATTRIBUTE_TYPE  VARCHAR2(100));

CREATE TABLE APPLES
  (APPLE_ID        NUMBER,
   APPLE_TYPE      VARCHAR2(100)
     CONSTRAINT APPLES_FK1
       REFERENCES APPLE_TYPE(APPLE_TYPE)
         ON DELETE CASCADE,
   ATTRIBUTE_TYPE  VARCHAR2(100)
     CONSTRAINT APPLES_FK2
       REFERENCES APPLE_ATTRIBUTES(ATTRIBUTE_TYPE)
         ON DELETE NO ACTION);

祝你好运。

【讨论】:

  • 你确定吗?看起来很奇怪,如果你的苹果有 20 个属性,你将不得不在每一行上重复你的 apple_type。或者我错过了什么
  • @LauDec 是的,“你必须在每一行重复你的 apple_type”。 But that fact alone is neither good nor bad..
  • @philipxy 感谢您的链接。有趣的阅​​读。通过查看实验人员的解决方案/意见并尝试了解与我选择的解决方案的差异,可以学到很多东西。
  • @LauDec 阅读信息建模和数据库设计的一些介绍。 许多 学术出版的教科书/演示文稿/课程可在线免费(pdf、ppt、mp4 等)。由于人们已经编写了相关概念的适当教程组织,尝试通过随机示例学习似乎不太有效......如果不是没有希望的话。例如:为什么说它“奇怪”?如果你根据你所知道的来检查它为什么“看起来”如此,你真的只是说它不符合你在你见过的例子中注意到的模式吗?但是我们可以从其他人的调查和概括中学到很多
  • 不清楚如何用这个来表达另一种设计,反之亦然。如果您的观点是从标签列表列移动到标签列,则无需引入两个表。
【解决方案4】:

抛开糟糕的表设计不谈,这可以使用like 评估来完成。

select
   apple
   tags
from
   table
where
   lower(tags) like '%tag_here%'

我在这里使用了lower() 函数来简化字符串大小写的处理。当您替换 tag_here 时,请使用所有小写字符。


话虽如此,您确实应该改进您的数据库设计。从存储和性能的角度来看,这都是非常低效的。更好的设计将有两个不同的表。一个存储苹果,第二个表将带有外键的标签存储回苹果表。

【讨论】:

  • @philipxy:如果不使用 apple_id,您希望如何在两个表之间加入?
  • 我以为您正在删除标签的标签列表。我现在明白了,我不知道你是否正在这样做。我的意思是这也被添加了对苹果的间接性来掩盖。
【解决方案5】:

我会将其中一些标签创建为列,并为“杂项”标签创建第二个表。

表:苹果 Apple_ID PK 姓名 Where_Grown 尺寸 表:Apple_Tags Tag_ID PK Apple_ID FK 标签 索引:Apple_Tags.Tag、Apple_ID Apple 1 的数据为: 苹果表 编号:1 名称:富士 Where_Grown:加利福尼亚 尺寸:中号 标签表 标签_ID:1 Apple_ID:1 标签:盒装

要查找标签:

select * from apples a inner join apple_tags t on a.apple_id = t.apple_id

请注意,我不会在一列中存储多个标签。这打破了列是原子的规范化的第一条规则。我将它们作为行存储在单独的表中。我也认识到苹果的名称、大小和种植地点是所有苹果共有的属性。

【讨论】:

  • 样本数据包含红色美味苹果作为 Apple3 和 Apple4。所以我选择使用我认为比 Apple3 和 Apple4 更好的密钥。
  • 我在之前的评论中没有明确表达我的观点:与问题最相关的设计问题(当然它甚至与设计无关)是使用标签列而不是标签列表列,但它掩盖了这一点以进行其他更改。 (尽管你很清楚做其他事情。我同意从给定的名称和标签使用来看,OP 应用程序的适当设计将包含更多结构。但问题中还不足以知道什么。 )
  • 明白。堆栈溢出的新手,但对开发来说并不陌生。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-11-11
  • 1970-01-01
  • 2022-11-22
  • 2020-06-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多