【问题标题】:SQL: Reduce table by removing specified Items in a tableSQL:通过删除表中的指定项目来减少表
【发布时间】:2017-01-10 14:16:55
【问题描述】:

我在 vertica db 中有下表:

+-------+-------+-------+
| Item1 | Item2 | Item3 |
+-------+-------+-------+
| A     | B     | S     |
| S     | C     | D     |
| E     | F     | S     |
+-------+-------+-------+

每一行代表一次交易(例如在商店购物)。我正在寻找一种可扩展的方法来删除表中的所有 S 项目,而不是减少表以获得此:

+-------+-------+
| Item1 | Item2 |
+-------+-------+
| A     | B     |
| D     | C     |
| E     | F     |
+-------+-------+

新表中的行顺序并不重要。

旁注:表格中的每一行都有S 项,因此无需担心null 项。

跟进:如果我想同时删除n个项目,有没有比运行n次问题的SQL查询更快的方法?

一种可能的解决方案是将术语0000 替换或添加到相关项目中。比按字母数字对每一行进行排序并删除第一列,但我想知道是否有更优雅的方式。

【问题讨论】:

  • 真正的问题是你的桌子设计。好像没有规范化
  • @juergend 你能详细说明一下吗?
  • 如果我们可以看到一个真实的例子,我们可以为更好的数据库结构提供一些建议。目前这看起来你没有遵循任何设计规则。
  • 每一行代表一个交易(例如在商店购物) 好吧,你需要更具体一些。为什么不用一点示例数据添加真实的表结构?
  • 这是真正的表结构,只有简化的值和更小的表。但一般的想法只是从每一行中删除一个条目。这在sql中不是微不足道的吗?例如,在 matlab 中,我将分两行执行此操作。

标签: sql vertica


【解决方案1】:

可能有多种方法可以做到这一点。我会先将S 转换为NULL。然后COALESCE 删除NULL 只留下两个值。然后我会使用LEASTGREATEST 对这两个值进行排序,以便得到一个干净的输出。

注意COALESCE 我只是颠倒了顺序,这样在所有情况下(假设每个元组只有一个S 值)这两个项目都是不同的。

with s_filtered as (
    select nullif(Item1,'S') Item1,
           nullif(Item2,'S') Item2,
           nullif(Item3,'S') Item3
    from mytable
)
select distinct least(coalesce(Item1,Item2)) Item1,
       greatest(coalesce(Item3,Item2)) Item2
from s_filtered

在这种情况下,我不确定我是否理解您问题的可扩展部分。如果您的意思是要拥有 3 个以上的项目,那么……这种方法不会很好用。你可以做到(减去那种你必须做的预处理),但这可能是很多创造性的合并。

或者,您可以在过滤掉 S 类型后进行规范化并进行某种分析。这会更好地支持更多的项目。

例如,如果您有 6 个项目和一个 S(请注意,您需要将所有项目与 id 联系在一起):

with ordered_mytable as (
    select id, item, row_number() over (partition by id order by item) rn
    from mytable
    where item <> 'S'
)
select id, 
       max(decode(rn,1,item)) Item1,
       max(decode(rn,2,item)) Item2,
       max(decode(rn,3,item)) Item3,
       max(decode(rn,4,item)) Item4,
       max(decode(rn,5,item)) Item5
from ordered_mytable
group by id

【讨论】:

  • 我在使用第二种方法时遇到了问题,即:将事物与 id 联系在一起。似乎在 vertica 的初始表中添加一个带有 id 的新列并非易事。也许我对“规范化”步骤感到困惑,它基本上是所有行的联合以获得两列,一列带有 id,一列带有数据?但为了实现这一点,我仍然需要先在初始表中添加一个 id 列,有没有比这更简单的方法:link
  • 如果它只是这个过程的临时性(就像你把它扔在一个临时/工作表中),你可以使用row_number() over () 也许(但这在多次执行中不是静态的,可能会有所不同每次运行)。取决于你在做什么,这可能行不通。或者是的,您可以添加一个标识列。如果你这样做了,那么你需要确保你的序列缓存设置得足够高,以避免频繁的非缓存拉取。
【解决方案2】:

好的,我设法以一种时髦的方式解决了问题。

  1. 使用||从所有行创建一个字符串

  2. 删除不需要的字符串部分

  3. 使用SPLIT_PART拆分字符串

这里是查询:

SELECT 
    SPLIT_PART(replace, ';', 2) AS c1, 
    SPLIT_PART(replace, ';', 3) AS c2 
FROM 
    (SELECT replace (  ';'|| item1 ||';'|| item2 ||';'|| item3  ,     ';S'    ,     '') 
     FROM my_table ) AS temp

在我看来,这个查询可以很容易地为更大的表生成(例如使用 python),并且不涉及创建临时表。

【讨论】:

  • 如果这些是 ASCII,则使用 SPLIT_PARTB。它更快。
  • 现在这些项目实际上是字符串 (varchar(300)),我正在考虑将它们转换为整数以加快处理速度。这加上使用SPLIT_PARTB 应该会使这个过程更快。感谢您的提示!
  • 整数可能有帮助,也可能没有。无论哪种方式,您都必须将它们转换为 varchar 。您将获得更多的节省在投影方面(尝试读取更少的数据等)。
猜你喜欢
  • 2016-06-26
  • 2016-02-12
  • 1970-01-01
  • 2021-03-12
  • 2019-01-08
  • 1970-01-01
  • 1970-01-01
  • 2021-06-16
  • 1970-01-01
相关资源
最近更新 更多