【问题标题】:Cassandra select CQL: Cannot add column after wildcardCassandra 选择 CQL:无法在通配符后添加列
【发布时间】:2021-10-03 13:18:38
【问题描述】:

我需要将写入时间戳作为大量表的表导出的一部分输出,但我完全想不出一种不强制我显式选择语句中的所有列的方法。

而不是能够做到这一点:

SELECT *, writetime(data) AS timestamp  FROM dls.licenses;

我必须这样做:

SELECT column1, column2, ... , writetime(data) AS timestamp  FROM dls.licenses;

这很不方便,因为这意味着每次任何表的架构发生变化时我都必须更改导出工具。

有没有更好的办法?

编辑:澄清一下,我得到的实际错误如下。错误中语法的呈现方式可能会认为SQL应该没问题:

SELECT *, writetime(id) AS timestamp  FROM dls.licenses;
SyntaxException: line 1:8 mismatched input ',' expecting K_FROM (SELECT *[,]...)

编辑 2:这是用于该表的键空间和创建语句:

CREATE KEYSPACE IF NOT EXISTS dls WITH replication = { 'class': 'SimpleStrategy', 'replication_factor': ‚1‘ };
CREATE TABLE IF NOT EXISTS dls.licenses (subscription_id text, id text, key text, data text, PRIMARY KEY (key));
CREATE INDEX IF NOT EXISTS ON dls.licenses (id);

顺便说一句:我正在使用新的 Cassandra 4.0.0 (GA)。

【问题讨论】:

    标签: cassandra cql


    【解决方案1】:

    如果您要导出为 CSV 或 JSON 文件,您可以考虑使用 DataStax 的 dsbulk。

    https://github.com/datastax/dsbulk

    dsbulk 1.8.0 最新版本增加了对导出 writetime 和 ttl 的支持。

    https://docs.datastax.com/en/dsbulk/doc/dsbulk/reference/schemaOptions.html#schemaOptions__schemaOptionsPreserveTimestamp

    dsbulk unload -url myData.csv -k ks1 -t table1 --timestamp
    

    【讨论】:

    • 这是一个 SQL 语法错误,不是所用工具的问题。我实际上也在这里使用 dsbulk 。虽然返回的错误与 cqlsh 和 dsbulk 相同。
    • 经过更多测试后,我可以确认它添加了时间戳列,但我必须使用“--dsbulk.schema.preserveTimestamp true”。然而,缺点是这将为每列添加一个时间戳列,而不是我需要的单个时间戳(例如,我知道数据字段也将被更新并且可以使用它的时间戳作为更改指示器)。
    • CQL 语法明智,如果使用 '*',则不允许放置任何其他内容。
    • 是的,它看起来像这样,但我认为这是一个错误,因为此语法甚至在错误消息中显示为示例:“SELECT *[,]...)”。
    【解决方案2】:

    WHERE 子句指定必须查询哪些行。它由作为 PRIMARY KEY 的一部分和/或在其上定义了二级索引的列上的关系组成。

    • 关系的列规范必须是以下之一:
    • 表的分区键的一个或多个成员
    • 集群列,仅当关系前面有其他关系指定分区键中的所有列
    • 使用 CREATE INDEX 编制索引的列。

    在 Cassandra 3.6 及更高版本中,添加 ALLOW FILTERING 以仅过滤非索引集群列。

    您可以通过在您希望写入时间的列上创建二级索引来解决您的查询问题。请记住,二级索引会产生开销,并可能导致意想不到的后果。

    【讨论】:

    • 使用主键列(id)还是会报错:SELECT *, writetime(id) AS timestamp FROM dls.licenses; SyntaxException: line 1:8 mismatched input ',' 期待 K_FROM (SELECT *[,]...)
    • @benjist,你能分享一下架构吗?
    • 看起来问题是关于 SELECT 子句,而不是 WHERE 子句。
    【解决方案3】:

    SELECT * 中的星号 (*) 是“所有列”的 CQL 语法,因此根据定义,不可能包含另一列,因为即使对于本机 CQL 函数也选择了所有列。因此,您需要枚举所有列名 + 列上的函数。

    +1 对 Yuki 的回答。我想补充一点,DSBulk 为表中的每一列添加了一个 WRITETIME() 列,因为在读取完整分区之前无法提前知道分区中每一列的写入时间。

    请允许我用几个例子来解释它。

    架构

    考虑这张表:

    CREATE TABLE users_by_email (
        email text,
        name text,
        address text,
        mobile text,
        PRIMARY KEY (email)
    )
    

    示例 1

    如果我们添加一个为所有列指定值的新记录:

    INSERT INTO users_by_email (email, name, address, mobile)
      VALUES ('alice@staysafe.com', 'Alice', '221B Baker St', '098-765-432-109');
    

    那么对于这个分区,所有列都将具有相同的写入时间。

    示例 2

    考虑一个记录在一段时间内被多次插入碎片化的情况,例如:

    INSERT INTO users_by_email (email, name)    VALUES ('dude@getvaccinated.now', 'Bob');
    
    INSERT INTO users_by_email (email, address) VALUES ('dude@getvaccinated.now', '350 Fifth Ave');
    
    INSERT INTO users_by_email (email, mobile)  VALUES ('dude@getvaccinated.now', '012-555-123-456');
    

    nameaddressmobile 的每一列都有不同的写入时间。

    从这 2 个示例中,您应该看到并不总是有一个写入时间适用于分区中的所有列。

    对于您的特定用例,您需要从 DSBulk 输出中找出哪个写入时间用于在不同时间插入/更新分区片段的情况。干杯!

    【讨论】:

    • 感谢您为 writetim 提供见解。尽管关于 *:* 在标准 SQL 中也表示“所有列”,此处相同。但是我发现 CQL 语法被设计破坏了,因为它对例如有效。选择所有列,后跟任意数量的选择器表达式(强制转换、函数、术语)。适用于标准 SQL,我不明白为什么它不应该在这里工作:选择 WRITETIME(column) 时的结果将是相同的,无论是否使用 *。我的意思是:随着时间的推移,一行中的列的写入时间可能会有所不同,但对于一行中的同一列则不会(毕竟“行”是由 pkey 存储的)。
    • 也许在我上面的评论之后,为什么第一次使用 Cassandra 用户(我)会被提到的错误消息“期望 K_FROM (SELECT *[,]...)”弄糊涂。它建议您可以使用 * 后跟例如一个函数(如在标准 SQL 中),而实际上 CQL EBNF 将其定义为“要么 * 或其他”,如您所提到的: „ SELECT [ JSON | DISTINCT ] ( select_clause | '*' )“。
    • 没错。 CQL 的语法与 SQL 不同——它是一个子集。干杯!
    猜你喜欢
    • 2018-02-21
    • 2013-08-28
    • 2016-04-14
    • 2014-03-23
    • 1970-01-01
    • 2017-01-14
    • 2014-01-03
    • 2013-07-15
    • 2013-09-13
    相关资源
    最近更新 更多