Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/ssh/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Sql 如何在where子句中为计算的筛选条件使用部分索引?_Sql_Postgresql - Fatal编程技术网

Sql 如何在where子句中为计算的筛选条件使用部分索引?

Sql 如何在where子句中为计算的筛选条件使用部分索引?,sql,postgresql,Sql,Postgresql,假设我有一个简单的问题: 解释分析 选择 计数* 从…起 观众a 哪里 a、 创建日期>=当前日期-间隔“5天”; 这是一个1GB+的表,在列上创建了部分索引。当我运行此查询时,它会进行顺序扫描,并且不会使用我的索引,这显然需要很多时间: Aggregate (cost=345853.43..345853.44 rows=1 width=8) (actual time=27126.426..27126.426 rows=1 loops=1) -> Seq Scan on audie

假设我有一个简单的问题:

解释分析 选择 计数* 从…起 观众a 哪里 a、 创建日期>=当前日期-间隔“5天”; 这是一个1GB+的表,在列上创建了部分索引。当我运行此查询时,它会进行顺序扫描,并且不会使用我的索引,这显然需要很多时间:

Aggregate  (cost=345853.43..345853.44 rows=1 width=8) (actual time=27126.426..27126.426 rows=1 loops=1)
  ->  Seq Scan on audiences a  (cost=0.00..345840.46 rows=5188 width=0) (actual time=97.564..27124.317 rows=8029 loops=1)
        Filter: (created_at >= (('now'::cstring)::date - '5 days'::interval))
        Rows Removed by Filter: 2215612
Planning time: 0.131 ms
Execution time: 27126.458 ms
另一方面,如果我有这样一个硬编码或预计算的值:

解释分析 选择 计数* 从…起 观众a 哪里 a、 创建时间>=时间戳“2020-10-16 00:00:00”; 它将利用在以下位置创建的指数:

Aggregate  (cost=253.18..253.19 rows=1 width=8) (actual time=1014.655..1014.655 rows=1 loops=1)
  ->  Index Only Scan using index_audiences_on_created_at on audiences a  (cost=0.29..240.21 rows=5188 width=0) (actual time=1.308..1011.071 rows=8029 loops=1)
        Index Cond: (created_at >= '2020-10-16 00:00:00'::timestamp without time zone)
        Heap Fetches: 6185
Planning time: 1.878 ms
Execution time: 1014.716 ms
如果可以的话,我会使用ORM生成一个具有正确值的查询,但我不能。是否有一种方法可以预先计算这个时间戳,并通过普通SQL在WHERE子句中使用它

添加一些我的设置的技术信息

PostgreSQL版本:9.6.11

在列类型处创建的\u为:timestamp


索引:在“2020-10-01 00:00:00”创建的树上创建的索引:没有时区的时间戳这不是确切的答案。但具体情况如何

如果筛选条件大于谓词条件,则在>'2020-10-01 00:00:00'处创建谓词::时间戳,不带时区。然后您可以在何处预先设置条件


注意:可能不是时间戳,您必须放置不带时区的时间戳或带时区的时间戳。取决于列类型这不是确切的答案。但具体情况如何

如果筛选条件大于谓词条件,则在>'2020-10-01 00:00:00'处创建谓词::时间戳,不带时区。然后您可以在何处预先设置条件


注意:可能不是时间戳,您必须放置不带时区的时间戳或带时区的时间戳。根据我的经验,最好的方法是创建一个PL函数。我认为问题在于,它正在评估每行当前的_日期间隔“5天”

因此,您必须创建一个PL,对其求值一次,然后将其用于查询。比如:

CREATE OR REPLACE FUNCTION count_audiences(
    vinterval text -- to send interval dynamically
)
RETURNS INTEGER
AS $$
DECLARE
    vdate timestamp;
    vcount integer := 0;
BEGIN
    EXECUTE 'SELECT current_date - INTERVAL '''||vinterval||'''' INTO vdate;-- obtain date
    SELECT
        COUNT(*)
    INTO
        vcount
    FROM
        audiences a
    WHERE
        a.created_at >= vdate;
    RETURN vcount;
END;
$$ LANGUAGE plpgsql;
创建PL后,只需像这样调用它:

SELECT * FROM count_audiences('5 days');

通过这种方式,您还可以使用ORM调用函数。

根据我的经验,最好的方法是创建PL函数。我认为问题在于,它正在评估每行当前的_日期间隔“5天”

因此,您必须创建一个PL,对其求值一次,然后将其用于查询。比如:

CREATE OR REPLACE FUNCTION count_audiences(
    vinterval text -- to send interval dynamically
)
RETURNS INTEGER
AS $$
DECLARE
    vdate timestamp;
    vcount integer := 0;
BEGIN
    EXECUTE 'SELECT current_date - INTERVAL '''||vinterval||'''' INTO vdate;-- obtain date
    SELECT
        COUNT(*)
    INTO
        vcount
    FROM
        audiences a
    WHERE
        a.created_at >= vdate;
    RETURN vcount;
END;
$$ LANGUAGE plpgsql;
创建PL后,只需像这样调用它:

SELECT * FROM count_audiences('5 days');

通过这种方式,您还可以使用ORM调用该函数。

在以下位置创建可用的索引:

Aggregate  (cost=253.18..253.19 rows=1 width=8) (actual time=1014.655..1014.655 rows=1 loops=1)
  ->  Index Only Scan using index_audiences_on_created_at on audiences a  (cost=0.29..240.21 rows=5188 width=0) (actual time=1.308..1011.071 rows=8029 loops=1)
        Index Cond: (created_at >= '2020-10-16 00:00:00'::timestamp without time zone)
        Heap Fetches: 6185
Planning time: 1.878 ms
Execution time: 1014.716 ms
输出:


如果在以下位置创建了可用的索引,则在此处工作:

Aggregate  (cost=253.18..253.19 rows=1 width=8) (actual time=1014.655..1014.655 rows=1 loops=1)
  ->  Index Only Scan using index_audiences_on_created_at on audiences a  (cost=0.29..240.21 rows=5188 width=0) (actual time=1.308..1011.071 rows=8029 loops=1)
        Index Cond: (created_at >= '2020-10-16 00:00:00'::timestamp without time zone)
        Heap Fetches: 6185
Planning time: 1.878 ms
Execution time: 1014.716 ms
输出:


您有一个部分索引,而优化器不够聪明,无法计算where子句中的表达式,然后根据表达式的结果选择部分索引


因此,除了在没有WHERE子句的情况下创建索引外,您无能为力。

您有一个部分索引,并且优化器不够聪明,无法计算WHERE子句中的表达式,然后根据表达式的结果选择部分索引



因此,除了创建一个没有WHERE子句的索引之外,您没有什么可以做的。

如果您修复类型使它们匹配:WHERE a.created_at>=current_date-INTERVAL'5 days'::timestamp.@GordonLinoff刚刚尝试了这个方法-同样的结果。@a_horse_在x86_64-pc-linux-gnu上没有名称PostgreSQL 9.6.11,由gcc gcc 4.8.3 20140911 Red Hat 4.8.3-9编制,64位你能发布你的索引吗,包括创建的_at@itzMEonTV我用关于索引的技术信息更新了这个问题。我在该列上只有一个索引。如果你修改了类型,使它们匹配:WHERE a.created\u at>=current\u date-INTERVAL'5 days'::timestamp。@GordonLinoff刚刚尝试过这个-同样的结果。@由gcc gcc 4.8.3 20140911 Red Hat 4.8.3-9,64位编译的x86\u 64-pc-linux-gnu上的一个名为PostgreSQL 9.6.11的\u horse\u可以发布包含已创建的_at@itzMEonTV我用关于索引的技术信息更新了这个问题,我在该列上只有一个索引,不需要函数。也太复杂了。不需要一个函数。也太复杂了。IMHOStill,我希望PG更聪明一点:虽然在PG 11中它看起来像预期的那样,但它的行为却和预期的一样。优化器非常聪明。当您将where放在索引而不是查询上时,您实际上告诉优化器不要使用此索引。优化器在计算当前_日期-间隔“5天”的值之前选择访问路径。因此,它无法知道索引中的where是否满足,因此必须选择不使用该索引。但该索引可能仅用于第一个条件,而第二个条件为

由滤波器实现。因此,索引就像一个分区。如果部分索引较大,则不会产生显著影响,Postgres也不会使用它。用“没有名字”的@a_horse_的答案当然是更好的方法。不过,我还是希望PG更聪明一点:虽然它看起来像在PG 11中,但它的行为与预期的一样。优化器非常聪明。当您将where放在索引而不是查询上时,您实际上告诉优化器不要使用此索引。优化器在计算当前_日期-间隔“5天”的值之前选择访问路径。因此,它无法知道索引中的where是否满足,因此必须选择不使用该索引。但该索引可能仅用于第一个条件,而第二个条件是通过过滤器实现的。因此,索引就像一个分区。如果部分索引较大,则不会产生显著影响,Postgres也不会使用它。用@a_horse_和\u no_名称来回答@a_horse_肯定是更好的方法。嘿,是的,正如我们所看到的,优化器不够聪明,但是事实证明,我们可以通过在where子句中预先添加索引谓词来强制它获取索引@DannyOcean:对于一个只有200万行的表,部分索引真的能改善那么多吗?为什么不简单地创建一个没有WHERE子句的索引呢?我想使用它,因为pg有这个功能,而且对我来说,拥有一个部分索引是完全合法的,因为我为一个特定的查询添加了它,而该查询不会在指定日期之前使用它。我没想到会有这样的行为,所以你可以在这里看到我的问题。如果我知道我需要这样做,也许我会创建一个常规索引。另外:看看这里的部分索引。嘿,是的,正如我们所看到的,优化器不够聪明,但是事实证明我们可以通过在where子句中预先添加索引谓词来强制它获取索引@DannyOcean:对于一个只有200万行的表,部分索引真的能改善那么多吗?为什么不简单地创建一个没有WHERE子句的索引呢?我想使用它,因为pg有这个功能,而且对我来说,拥有一个部分索引是完全合法的,因为我为一个特定的查询添加了它,而该查询不会在指定日期之前使用它。我没想到会有这样的行为,所以你可以在这里看到我的问题。如果我知道我需要这样做,也许我会创建一个常规索引。另外:看看这里的部分索引。你的索引是部分的吗?这是部分索引的一个问题。否。它是{created_at,user_id}上的复合索引。OP对他的索引不是很清楚你的索引部分?这是部分索引的一个问题。否。它是{created_at,user_id}上的复合索引。OP对他的索引不是很清楚