窗口函数或公共表表达式:计算范围内的前几行

2024-04-12

我想使用窗口函数来确定每一行满足特定条件的先前记录的总数。

一个具体的例子:

clone=# \d test
              Table "pg_temp_2.test"
 Column |            Type             | Modifiers 
--------+-----------------------------+-----------
 id     | bigint                      | 
 date   | timestamp without time zone | 

我想知道每个date'之前 1 小时内'的行数date.

我可以用窗口函数来做到这一点吗?或者我需要调查 CTE 吗?

我真的希望能够写出类似的东西(不起作用):

SELECT id, date, count(*) OVER (HAVING previous_rows.date >= (date - '1 hour'::interval))
FROM test;

我可以通过加入针对自身的测试来编写此内容,如下所示 - 但这不会随着特别大的表而扩展。

SELECT a.id, a.date, count(b.*)-1 
FROM test a, test b 
WHERE (b.date >= a.date - '1 hour'::interval AND b.date < a.date)
GROUP BY 1,2
ORDER BY 2;

我可以用递归查询来做这件事吗?还是常规 CTE? 我对 CTE 还不是很了解。我有预感我很快就会去。 :)


我认为你不能用简单的查询、CTE 和窗口函数便宜地做到这一点 - 它们的框架定义是静态的,但你需要一个动态框架(取决于列值)。

一般来说,您必须仔细定义窗口的下限和上限:以下查询exclude当前行和include下边框。
仍然有一个细微的差别:该函数包括当前行的前一个同级,而相关子查询则排除它们......

测试用例

Using ts而不是保留字date作为列名。

CREATE TABLE test (
  id  bigint
, ts  timestamp
);

ROM - 罗曼的查询 https://stackoverflow.com/a/18175342/939860

使用 CTE,将时间戳聚合到数组中,取消嵌套,计数...
虽然正确,但性能急剧恶化有一手多的行。这里有几个性能杀手。见下文。

ARR - 计数数组元素

我接受了 Roman 的查询并尝试简化它:

  • 删除不必要的第二个 CTE。
  • 将第一个 CTE 转换为子查询,速度更快。
  • Direct count()而不是重新聚合到数组中并用array_length().

但数组处理成本高昂,而且性能仍然不佳严重恶化有更多行。

SELECT id, ts
     , (SELECT count(*)::int - 1
        FROM   unnest(dates) x
        WHERE  x >= sub.ts - interval '1h') AS ct
FROM (
   SELECT id, ts
        , array_agg(ts) OVER(ORDER BY ts) AS dates
   FROM   test
   ) sub;

COR-- 相关子查询

You could用一个简单的相关子查询来解决它。速度快了很多,但仍然...

SELECT id, ts
     , (SELECT count(*)
        FROM   test t1
        WHERE  t1.ts >= t.ts - interval '1h'
        AND    t1.ts < t.ts) AS ct
FROM   test t
ORDER  BY ts;

FNC-功能

按时间顺序循环行row_number() in plpgsql 函数并将其与cursor https://www.postgresql.org/docs/current/plpgsql-cursors.html在同一查询上,跨越所需的时间范围。然后我们可以减去行数:

CREATE OR REPLACE FUNCTION running_window_ct(_intv interval = '1 hour')
  RETURNS TABLE (id bigint, ts timestamp, ct int)
  LANGUAGE plpgsql AS
$func$
DECLARE
   cur   CURSOR FOR
         SELECT t.ts + _intv AS ts1, row_number() OVER (ORDER BY t.ts) AS rn
         FROM   test t ORDER BY t.ts;
   rec   record;
   rn    int;

BEGIN
   OPEN cur;
   FETCH cur INTO rec;
   ct := -1;  -- init

   FOR id, ts, rn IN
      SELECT t.id, t.ts, row_number() OVER (ORDER BY t.ts)
      FROM   test t ORDER BY t.ts
   LOOP
      IF rec.ts1 >= ts THEN
         ct := ct + 1;
      ELSE
         LOOP
            FETCH cur INTO rec;
            EXIT WHEN rec.ts1 >= ts;
         END LOOP;
         ct := rn - rec.rn;
      END IF;

      RETURN NEXT;
   END LOOP;
END
$func$;

默认一小时间隔的呼叫:

SELECT * FROM running_window_ct();

或任意间隔:

SELECT * FROM running_window_ct('2 hour - 3 second');

db<>fiddle
Old sqlfiddle http://www.sqlfiddle.com/#!17/5faffd/1

基准

根据上面的表格,我在我的旧测试服务器上运行了一个快速基准测试:(Debian 上的 PostgreSQL 9.1.9)。

-- TRUNCATE test;
INSERT INTO test
SELECT g, '2013-08-08'::timestamp
         + g * interval '5 min'
         + random() * 300 * interval '1 min' -- halfway realistic values
FROM   generate_series(1, 10000) g;

CREATE INDEX test_ts_idx ON test (ts);
ANALYZE test;  -- temp table needs manual analyze

我改变了bold每轮比赛的一部分,并取得 5 场最佳成绩EXPLAIN ANALYZE.

100 rows
ROM:27.656 毫秒
ARR:7.834 毫秒
响应时间:5.488 毫秒
FNC: 1.115 ms

1000 行
ROM:2116.029 毫秒
ARR:189.679 毫秒
响应时间:65.802 毫秒
FNC: 8.466 ms

5000行
ROM:51347 毫秒!
ARR:3167 毫秒
响应时间:333 毫秒
FNC: 42 ms

100000 行
游戏ROM:地下城与勇士
到达时间:DNF
响应时间:6760 毫秒
FNC: 828 ms

函数是明显的胜利者。它速度最快一个数量级,并且扩展性最好。
数组处理无法竞争。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

窗口函数或公共表表达式:计算范围内的前几行 的相关文章

  • sql查询连接两个服务器中不同数据库的两个表

    我在 ServerS 上的数据库中有两个表 tableA 在 ServerB 上的数据库中有两个表 我只想根据这些表的公共字段名对这些表执行 fullouter join 在 SQL Server 中 您可以创建一个链接服务器 在 Mana
  • sqlalchemy 的 row_to_json 语法

    我想弄清楚如何将 Postgres 9 2 row to json 与 SqlAlchemy 一起使用 但是我无法想出任何有效的语法 details foo row q select Foo where Foo bar id Bar id
  • BULK INSERT 中格式附近的语法不正确?

    我试图找出为什么我使用的 BULK INSERT 命令无法识别命令中使用的 FORMAT 和 FIELDQUOTE 选项 BULK INSERT dbo tblM2016 RAW Current Import File FROM x tms
  • 是否可以从数据库转储生成 knex 种子文件?

    就我而言 我使用的是 mysql 但是 我正在寻找一种通用解决方案 用于从当前运行的数据库或数据库转储生成 knex 种子文件 我可以就像是 https github com tgriesser knex issues 944 issuec
  • 在 Oracle SQL 中执行 MERGE 时,如何更新 SOURCE 中不匹配的行?

    我有一个main数据库和一个report数据库 我需要同步一个表main into report 但是 当项目在main数据库 我只想设置一个IsDeleted标志在report数据库 执行此操作的优雅方法是什么 我目前正在使用 MERGE
  • 显示多个表的账户余额

    我有以下两个表 其中存储有关贷记和借记记录的信息 couponCr 表包含 voucherType voucherPrefix voucherNo crparty cramount SALES S 1 1 43000 SALES S 2 1
  • 当列的数据类型为 int 时,如何用字符串替换 null

    我有一个包含 3 列的表和如下示例数据 所有列都是数据类型int 我有这个查询 select foodid dayid from Schedule 我要更换dayid用字符串 ifdayid null 为此我尝试了这个查询 select f
  • 如何重命名 SQL Server 中名称中带有方括号的内容?

    我的一张桌子上有一列 周围有方括号 Book Category 我想重命名为Book Category 我尝试了以下查询 sp rename BookPublisher Book Category Book Category COLUMN
  • 产品和变体 - 设计数据库的最佳方法

    描述 商店可以有产品 鞋子 T 恤等 每个产品可以有许多变体 每个变体可以有不同的价格和库存 例如T 恤有不同的颜色和尺寸 颜色 蓝色 尺寸 L 价格 10 美元 库存 5 颜色 蓝色 尺寸 XL 价格 10 美元 库存 10 颜色 白色
  • 如何比较表中最后一个和倒数第二个条目的值?

    我在 Oracle 中有一个名为quotes 的表 其中包含两列 date 和value 我想比较表中最后一个条目和倒数第二个条目的值 在此示例中 我想获取日期13 1 和 11 1在一行中以及每个日期的值之间的差异 10 5 5 报价表
  • SQL:将现有列设置为 MySQL 中的主键

    我有一个包含 3 列的数据库 id name somethingelse 该表没有设置索引 我收到 未定义索引 在 phpmyadmin 中id 是一个 7 位字母数字值 每行都是唯一的 我想将 Drugid 设置为主键 索引 我不知道有没
  • 获取 Postgres 数据库中每个表的行数

    获取数据库中所有表的行数的最有效方法是什么 我正在使用 Postgres 数据库 结果示例 table name row count some table 1 234 foobar 5 678 another table 32 如果您想要特
  • 将 PostgreSQL 中的 IP 地址转换为整数?

    有没有一个查询可以完成这个任务 例如 给定一个条目 216 55 82 34 我想用 分割字符串 并应用等式 IP 号 16777216 w 65536 x 256 y z 其中 IP 地址 w x y z 仅通过查询就可以实现这一点吗 您
  • 如何在Oracle数据库11g中创建新模式/新用户?

    我已经申请了一家公司的实习机会 作为一个问题 他们要求我为他们的公司创建一个具有一定要求的架构 并将DDL文件 我已经安装了 Oracle 数据库 11g Express 版本 但如何在 Oracle 数据库 11g 中创建新架构 我在网上
  • 如何查询多对多表(一个表的值成为列标题)

    给定此表结构 我想展平多对多关系 并将一个表的名称字段中的值设置为列标题 并将同一表中的数量设置为列值 目前可行的想法是将值放入字典 哈希表 中并用代码表示这些数据 但我想知道是否有 SQL 方法可以做到这一点 我还使用 Linq to S
  • PostgreSQL:在所有表字段的长度上创建索引

    我有一张桌子叫profile 我想按照填写最多的内容对它们进行排序 每列都是 JSONB 列或 TEXT 列 我不需要很大程度的确定性 所以通常我会按如下方式订购 SELECT FROM profile ORDER BY LENGTH CO
  • 在PostgreSQL中使用查询设置列类型

    优秀后answer https stackoverflow com questions 4336259 how to query the schema details of a table in postgres作者 Alexandre G
  • 如何使用 libpq 获取双精度值?

    The examples http www postgresql org docs 9 3 interactive libpq example htmllibpq 文档中展示了如何通过将整数值转换为主机字节序表示来获取整数值 我很好奇必须做
  • 列的 SQL MAX(包括其主键)

    Short 从下面的 sql select 中 我获取了 cart id 和该购物车中最高价值商品的值 SELECT CartItems cart id MAX ItemValues value FROM CartItems INNER J
  • 多个数据库连接

    我有三张桌子 categories content info and content The categories表包含类别的id及其 IDparent类别 The content info包含两列 entry id帖子的 ID 和cat

随机推荐

  • Hibernate 中的小写注释

    hibernate中有没有办法将实体的列注释为字符串小写 我的意思是例如 Entity public class User Column private String username 我希望休眠在所有未绑定到特定数据库的查询中将用户名转换
  • 如何在 gdb 中附加进程

    我有一个简单的 C 程序 它分叉一个进程 然后运行一个可执行文件 我想将子进程附加到 gdb 我在控制台中运行主程序并打开另一个控制台来查找子进程的 pid 然后使用以下命令启动 gdb gdb attach 12271 where 122
  • 不需要的空白导致列扭曲

    我正在尝试从间隔 非制表符 的 txt 文件导入化学品列表 NO FORMULA NAME CAS No A B C D TMIN TMAX code ngas TMIN ngas 25 C ngas TMAX 1 CBrClF2 brom
  • 如何在函数声明中声明 C++ mem_fn(member_function) ?

    我理解将成员函数的地址传递到类之外的基本问题 我感觉 mem fn 可能是解决方案 但我在具体细节上遇到了麻烦 我在类 p 中有一个成员函数 当前声明为 typedef void valNamedFlagsCallback const in
  • 创建半透明或透明窗口从透明到鼠标事件,添加到表单的控件除外

    您好 我试图获得像玻璃一样的透明形式 它可以使点击和每个鼠标事件传递到玻璃后面的窗口或项目 这是我用 Windows 窗体编写的代码 namespace ClickThroughMe public partial class ClickTh
  • 错误 1 ​​无法将类型“double”隐式转换为“int”。存在显式转换(您是否缺少转换?)[关闭]

    Closed 这个问题是无法重现或由拼写错误引起 help closed questions 目前不接受答案 我的 C 代码有一个错误 他们说 错误 1 无法将类型 double 隐式转换为 int 存在显式转换 是否缺少强制转换 错误在哪
  • 何时在 Java 中使用“this”

    我为我的琐碎且可能愚蠢的问题道歉 但我对在使用方法或访问某些内容时何时使用 this 前缀感到有点困惑 例如 如果我们看 4 这里 http apcentral collegeboard com apc public repository
  • 按钮和 GLSurfaceView

    我有一个 GLSurfaceView 我在其中使用 OpenGL 显示一些动画 我现在想向该视图添加一个按钮 这是如何实现的 不涉及xml布局可以做到吗 您可以手动构建视图并将其添加到活动的内容视图中 在 GLSurfaceView 上执行
  • 从 RDF 节点拉出字符串

    在使用 Jena 的 SPARQL 查询时 我试图以更易读的格式获取数据 但是我不知道如何以正确的方式提取数据 就目前而言 输出是 http www w3 org TR 2003 PR owl guide 20031209 wine Sau
  • 错误:增强禁用线程

    我的 boost 库有一些问题 我正在使用 freebsd 并使用 ports 安装了我的 boost Boost 版本是 1 45 我使用 g 47 作为编译器 另外 我从未在那里定义过 BOOST DISABLE THREADS usr
  • PHP json_decode 表示法问题

    我在处理我尝试解析的 JSON 文件中使用的符号时遇到问题 一些节点有 句点 在转义对象表示法的名称中 json article gt rssFeed url 我将如何选择节点 我是否需要 str replace 句点 或者是否可以使用其他
  • 如何通过 Xcode Designer 添加 MPVolumeView?

    我最初将 MPVolumeView 动态添加到页面上 import MediaPlayer MPVolumeView h IBAction handleVolumeButtonClicked id sender if volumeView
  • 这个 C++ 程序有什么问题?

    当我编译这个程序时 include
  • ROCKSDB 由于rocksdb_max_row_locks 无法获取锁

    我尝试将 CSV 加载到 Rocksdb 数据库中 但失败并显示以下错误 Got error 10 Operation aborted Failed to acquire lock due to rocksdb max row locks
  • 图像尺寸(drawable-hdpi/ldpi/mdpi/xhdpi)

    研究android 现在我有一些图像要放入可绘制目录 hdpi ldpi mdpi xhdpi 中 我读了很多 但我还是不明白 每个目录的图像尺寸应该是多少 希望这会有所帮助 mdpi 是参考密度 也就是说 mdpi 显示屏上的 1 像素等
  • 如何在引导模式上设置本地存储?

    modal 2 id 打开调查模式 我想要的只是这个特定的模式 在有人单击关闭按钮后每 24 小时重新出现一次 document ready function var modals events if window location has
  • 类型声明之前的问号(?)在php(?int)中意味着什么[重复]

    这个问题在这里已经有答案了 我在中看到过这段代码https github com symfony symfony blob master src Symfony Component Console Output Output php htt
  • 以编程方式添加项目引用

    基本上 在阅读了几个链接后 我尝试了上面的代码 foreach EnvDTE Project proj in soln Projects if proj Name BLL VSLangProj VSProject vsproj VSLang
  • Apple Mach-O 链接器错误 lxml

    我刚刚将 xcode 升级到 4 5 现在编译时出现错误 ld library not found for lxml2 2 7 3 cland error linker command failed with exit code 1 升级后
  • 窗口函数或公共表表达式:计算范围内的前几行

    我想使用窗口函数来确定每一行满足特定条件的先前记录的总数 一个具体的例子 clone d test Table pg temp 2 test Column Type Modifiers id bigint date timestamp wi