如果零匹配,如何使ClickHouse count()函数返回0?

时间:2017-06-01 12:45:10

标签: clickhouse

我们的任务是在ClickHouse中运行一批~20000个查询,并将结果存储到CSV文件中。每个查询都是一个count()聚合,返回一个数字。

我们这样做:

./generate_queries.js | clickhouse-client --multiquery | tr '\n' ',' >> metrics.csv

(是的,尾随逗号,我们会解决这个问题。)

查询示例:

SELECT count(*) FROM merged_data WHERE business_type = 22;

问题是如果查询匹配零记录,ClickHouse只返回任何内容,结果CSV文件中的记录数与查询数不同。

这可能是SQL的标准行为,但我们如何解决这个问题并使ClickHouse count()在零匹配的情况下返回0?

3 个答案:

答案 0 :(得分:2)

现在你可以这样做:

SELECT
    count() - 1
FROM (
    SELECT
        business_type
    FROM
        merged_data
    WHERE
        business_type = 22
    UNION ALL
        toUInt64(1)
)

只需将toUInt64替换为business_type的任何类型。

开发人员已经意识到了这个问题并且正在努力解决这个问题: https://github.com/yandex/ClickHouse/issues/51 https://groups.google.com/forum/#!topic/clickhouse/2JS_yzvYAHM

目前的计划是添加一种从设置中控制此行为的功能。

答案 1 :(得分:1)

我们设法使用JSON输出格式来解决这个问题。

SELECT count(*) FROM merged_data WHERE business_type = 22 FORMAT JSONCompact;

这种情况下的数据库响应如下:

{
    "meta":
    [
        {
            "name": "count()",
            "type": "UInt64"
        }
    ],

    "data":
    [

    ],

    "rows": 0,

    "statistics":
    {
        "elapsed": 0.044646461,
        "rows_read": 53413865,
        "bytes_read": 53413865
    }
}

即使是空匹配,总会有回复(如果是这种情况,我们会看到"rows": 0。)

JSON响应流解析器​​如下所示:

var readLine = require('readline');

var rl = readLine.createInterface({
  input: process.stdin,
  output: process.stdout,
  terminal: false
});

var buf = '';

rl.on('line', line => {
  buf += line;

  if (line == '}') {
    // End of JSON => process.
    var json = JSON.parse(buf);
    buf = '';

    if (json.rows === 0) {
      console.log('0');
    }
    else {
      console.log(json.data[0][0]);
    }
  }
});

答案 2 :(得分:0)

这是ClickHouse的已知问题。 要解决此问题,请按照以下步骤操作。

假设你的计数查询是这样的:

SELECT count(*) AS count
FROM mytable

将您的查询放入此sql模板:

SELECT *
FROM 
(
    -- put your cunt query here 
    UNION ALL 
    SELECT toUInt64(0)
) 
LIMIT 1

所以最终的查询将是:

SELECT *
FROM 
(
    SELECT count(*) AS count
    FROM mytable
    UNION ALL 
    SELECT toUInt64 (0)
) 
LIMIT 1

注意:使用此解决方法没有性能开销。