提高查询性能(仅限 C++)

在评估查询时,Vertica 可以利用有关值范围的可用信息。例如,如果数据已分区并且查询通过分区值限制输出,则 Vertica 可以忽略不可能包含满足查询的数据的分区。同样,对于标量函数,Vertica 可以不处理数据中函数返回的值不可能影响结果的行。

考虑一个表(表中包含数百万行客户订单数据)和一个标量函数(该函数计算为订单中的所有项目支付的总价格)。查询使用 WHERE 子句将结果限制为高于给定值的订单。对数据块调用标量函数;如果该块中没有行可以产生目标值,则跳过该块的处理可以提高查询性能。

用 C++ 编写的标量函数可以实施 getOutputRange 方法。在调用 processBlock 之前,Vertica 调用 getOutputRange 以在给定输入范围的情况下确定此块的最小和最大返回值。然后,它决定是否调用 processBlock 来执行计算。

Add2Ints 示例实施了此函数。最小输出值是两个输入的最小值之和,最大输出值是每个输入的最大值之和。此函数不考虑单个行。考虑以下输入:

   a  |  b
------+------
  21  | 92
 500  | 19
 111  | 11

两个输入的最小值是 21 和 11,因此函数将 32 报告为输出范围的下限。最大输入值为 500 和 92,因此它将 592 报告为输出范围的上限。任何输入行的返回值均大于 32,小于 592。

getOutputRange 的目的是快速消除输出肯定超出范围的调用。例如,如果查询包含 "WHERE Add2Ints(a,b) > 600",则可以跳过该数据块。仍然可能存在调用 getOutputRangeprocessBlock 没有返回结果的情况。如果查询包含 "WHERE Add2Ints(a,b) > 500",则 getOutputRange 不会消除此数据块。

Add2Ints 实施 getOutputRange,如下所示:


    /*
     * This method computes the output range for this scalar function from
     *   the ranges of its inputs in a single invocation.
     *
     * The input ranges are retrieved via inRange
     * The output range is returned via outRange
     */
    virtual void getOutputRange(Vertica::ServerInterface &srvInterface,
                                Vertica::ValueRangeReader &inRange,
                                Vertica::ValueRangeWriter &outRange)
    {
        if (inRange.hasBounds(0) && inRange.hasBounds(1)) {
            // Input ranges have bounds defined
            if (inRange.isNull(0) || inRange.isNull(1)) {
                // At least one range has only NULL values.
                // Output range can only have NULL values.
                outRange.setNull();
                outRange.setHasBounds();
                return;
            } else {
                // Compute output range
                const vint& a1LoBound = inRange.getIntRefLo(0);
                const vint& a2LoBound = inRange.getIntRefLo(1);
                outRange.setIntLo(a1LoBound + a2LoBound);

                const vint& a1UpBound = inRange.getIntRefUp(0);
                const vint& a2UpBound = inRange.getIntRefUp(1);
                outRange.setIntUp(a1UpBound + a2UpBound);
            }
        } else {
            // Input ranges are unbounded. No output range can be defined
            return;
        }

        if (!inRange.canHaveNulls(0) && !inRange.canHaveNulls(1)) {
            // There cannot be NULL values in the output range
            outRange.setCanHaveNulls(false);
        }

        // Let Vertica know that the output range is bounded
        outRange.setHasBounds();
    }

如果 getOutputRange 产生错误,Vertica 会发出警告并且不会为当前查询再次调用该方法。