标签: cte

优化 CTE 层次结构

下面更新

我有一个具有典型帐户/父帐户体系结构的帐户表来表示帐户层次结构(SQL Server 2012)。我使用 CTE 创建了一个 VIEW 来散列层次结构,总的来说它工作得很好,而且符合预期。我可以查询任何级别的层次结构,并轻松查看分支。

有一个业务逻辑字段需要作为层次结构的函数返回。每个帐户记录中的一个字段描述了企业的规模(我们将其称为 CustomerCount)。我需要报告的逻辑需要从整个分支汇总 CustomerCount。换句话说,给定一个帐户,我需要将该帐户的 customercount 值与层次结构中帐户下方每个分支中的每个子项相加。

我使用 CTE 中构建的层次结构字段成功计算了该字段,该字段看起来像 acct4.acct3.acct2.acct1。我遇到的问题只是让它运行得很快。如果没有这个计算字段,查询会在大约 3 秒内运行。当我添加计算字段时,它变成了一个 4 分钟的查询。

这是我能想出的最好的版本,它返回正确的结果。我正在寻找有关如何在不牺牲性能的情况下重新构建此视图的想法。

我理解这个变慢的原因(需要在 where 子句中计算一个谓词),但我想不出另一种方法来构造它并且仍然得到相同的结果。

下面是一些示例代码,用于构建表并执行 CTE,这与它在我的环境中的工作方式几乎完全一样。

Use Tempdb
go
CREATE TABLE dbo.Account
(
   Acctid varchar(1) NOT NULL
    , Name varchar(30) NULL
    , ParentId varchar(1) NULL
    , CustomerCount int NULL
);

INSERT Account
SELECT 'A','Best Bet',NULL,21  UNION ALL
SELECT 'B','eStore','A',30 UNION ALL
SELECT 'C','Big Bens','B',75 UNION ALL
SELECT 'D','Mr. Jimbo','B',50 UNION ALL
SELECT 'E','Dr. John','C',100 UNION ALL
SELECT 'F','Brick','A',222 UNION …
Run Code Online (Sandbox Code Playgroud)

sql-server optimization cte sql-server-2012

15
推荐指数
2
解决办法
2446
查看次数

PostgreSQL 递归后裔深度

我需要从它的祖先计算后代的深度。当一个记录有 时object_id = parent_id = ancestor_id,它被认为是一个根节点(祖先)。我一直在尝试WITH RECURSIVE使用 PostgreSQL 9.4运行查询。

我不控制数据或列。数据和表架构来自外部源。该表正在不断增长。现在每天大约有 3 万条记录。树中的任何节点都可能丢失,并且它们将在某个时候从外部源中拉出。它们通常按created_at DESC顺序拉取,但数据是通过异步后台作业拉取的。

我们最初对这个问题有一个代码解决方案,但现在有 500 万行以上,几乎需要 30 分钟才能完成。

示例表定义和测试数据:

CREATE TABLE objects (
  id          serial NOT NULL PRIMARY KEY,
  customer_id integer NOT NULL,
  object_id   integer NOT NULL,
  parent_id   integer,
  ancestor_id integer,
  generation  integer NOT NULL DEFAULT 0
);

INSERT INTO objects(id, customer_id , object_id, parent_id, ancestor_id, generation)
VALUES (2, 1, 2, 1, 1, -1), --no parent yet
       (3, 2, …
Run Code Online (Sandbox Code Playgroud)

postgresql cte update recursive

15
推荐指数
1
解决办法
4303
查看次数

如何以扩展的树状方式对递归查询的结果进行排序?

假设您有这样的nodes表:

CREATE TABLE nodes
(
    node serial PRIMARY KEY,
    parent integer NULL REFERENCES nodes(node),
    ts timestamp NOT NULL DEFAULT now()
);
Run Code Online (Sandbox Code Playgroud)

它代表了一个标准的类似节点的树结构,根节点在顶部,几个子节点悬挂在根节点或其他子节点上。

让我们插入几个示例值:

INSERT INTO nodes (parent)
VALUES (NULL), (NULL), (NULL), (NULL), (1), (1), (1), (1), (6), (1)
     , (6), (9), (6), (6), (3), (3), (3), (15);
Run Code Online (Sandbox Code Playgroud)

现在我想检索前 10 个根节点及其所有子节点,深度为 4:

WITH RECURSIVE node_rec AS
(
    (SELECT 1 AS depth, * FROM nodes WHERE parent IS NULL LIMIT 10)

    UNION ALL

    SELECT depth + 1, n.*
    FROM nodes …
Run Code Online (Sandbox Code Playgroud)

postgresql cte order-by recursive

14
推荐指数
1
解决办法
1万
查看次数

WITH CTE 和 WITH CTE (<column_names>) 之间有什么区别?

如在 MSDN 上使用公用表表达式中所示,您可以将 CTE 定义为:

WITH expression_name [ ( column_name [,...n] ) ]
AS
( CTE_query_definition )
Run Code Online (Sandbox Code Playgroud)

并使用它:

SELECT <column_list> FROM expression_name;
Run Code Online (Sandbox Code Playgroud)

假设我有以下 2 个 CTE

with cte1 as(
select name from Table1
)

with cte2(name) as(
select name from Table1
)
Run Code Online (Sandbox Code Playgroud)

一个查询为两个 CTE 输出相同的结果,因为内部查询是相同的。这两者之间的唯一区别是cte2(name)在其声明中定义了列名( )。

当我执行两个 CTE 时,我看不到执行计划有任何不同。

我只是想知道:

  • 如果我不在 CTE 定义中指定任何列名,这有什么区别?
  • 为什么我应该/不应该在创建 CTE 时指定列名?
  • 它是否会影响查询执行计划?(就我所见,它没有任何区别。)

sql-server cte sql-server-2014

12
推荐指数
3
解决办法
1万
查看次数

用一个查询插入多个表

我想通过一个查询将数据添加到至少三个表中。我想到了这样的事情:

WITH ins AS (
  INSERT INTO core.adr
    (street, "number", postal_code, city)
  VALUES
    ('test 1', '25a', '00912', 'villageman')
  RETURNING id)
INSERT INTO core.adr_information
  (idref, info)
SELECT id, 'test data'
FROM ins;
Run Code Online (Sandbox Code Playgroud)

这完全适用于两个表。

这里的主要问题是,所有其他查询都需要id第一个INSERT查询的值,这似乎无法通过这种方式进行管理。我认为它可以通过存储过程或事务轻松完成,但我希望解决方案是一个简单而可靠的查询。

我是否忽略了什么?是否有可能以这种方式或另一种方式(没有存储过程*或事务)?


笔记

* Altough,严格来说,Postgres没有存储过程,只有功能,@Erwin Brandstetter修改正确地通过评论中指出。有关相关信息,请参阅本系列帖子

postgresql cte

12
推荐指数
1
解决办法
6万
查看次数

CTE:让所有父母和所有孩子都在一个声明中

我有这个有效的 CTE 示例。

我可以选择所有祖父母和所有孩子。

但是如何在一个语句中选择所有祖父母和所有孩子?

在这个例子中,如果我给“父亲”作为输入,我想要祖父,父亲,儿子作为输出。

我使用 PostgreSQL。但我认为这个问题应该是标准的SQL。

如果我使用 PostgreSQL 特定的语法,请纠正我。

DROP table if exists tree;

CREATE TABLE tree (
 id SERIAL PRIMARY KEY,
 name character varying(64) NOT NULL,
 parent_id integer REFERENCES tree NULL
);

insert into tree values (1, 'Grandfather', NULL);
insert into tree values (2, 'Father', 1);
insert into tree values (3, 'Son', 2);


-- -------------------------------------
-- Getting all children works  

WITH RECURSIVE rec (id) as
(
  SELECT tree.id, tree.name from tree where name='Father'

  UNION ALL

  SELECT tree.id, …
Run Code Online (Sandbox Code Playgroud)

postgresql cte recursive

12
推荐指数
1
解决办法
2万
查看次数

如何从 SQL Server 审计数据中过滤掉标量值用户定义函数的使用情况?

我们有一个 SQL Server 数据库,它有一个数据库审计规范,它审计对数据库的所有执行操作。

CREATE DATABASE AUDIT SPECIFICATION [dbAudit]
FOR SERVER AUDIT [servAudit]
ADD (EXECUTE ON DATABASE::[DatabaseName] BY [public])
Run Code Online (Sandbox Code Playgroud)

我们发现,某些查询会将结果集中的每一行的标量函数的使用写入审计日志。当这种情况发生时,日志在我们可以 ETL 到它的最终休息位置之前就被填满了,我们的日志记录出现了空白。

不幸的是,由于合规性原因,我们不能简单地停止审核每条EXECUTE语句。

我们解决这个问题的第一个想法是使用Server AuditWHERE上的子句来过滤掉活动。代码如下所示:

WHERE [object_id] not in (Select object_id from sys.objects where type = 'FN' )
Run Code Online (Sandbox Code Playgroud)

不幸的是,SQL Server 不允许关系 IN 运算符(可能是因为它不想在每次必须写入审核日志时进行查询)。

我们想避免编写一个存储过程,其硬代码object_idWHERE条款,但是这是我们在解决这个问题的最好办法目前的想法。是否有我们应该考虑的替代方法?

我们注意到,当在递归 CTE 中使用标量函数时,它会导致查询为结果集中的每一行写入审计日志。

有一些标量值函数由供应商提供,我们无法删除或移动到备用数据库。

sql-server audit cte functions recursive

12
推荐指数
1
解决办法
929
查看次数

为什么 CTE 对丢失的更新开放?

我不明白 Craig Ringer 在评论时的意思:

如果插入事务回滚,此解决方案可能会丢失更新;没有强制执行 UPDATE 影响任何行的检查。

/sf/answers/609160401/ 上。请提供一个示例事件序列(例如,线程 1 执行 X,线程 2 执行 Y)以演示丢失更新是如何发生的。

postgresql cte concurrency upsert

11
推荐指数
1
解决办法
5124
查看次数

带有类型修饰符的数据类型的惊人结果

在讨论这个问题的递归 CTE 解决方案时:

@ypercube偶然发现了一个令人惊讶的异常,这导致我们调查类型修饰符的处理。我们发现了令人惊讶的行为。

1. 类型转换在某些上下文中保留了类型修饰符

即使被指示不要这样做。最基本的例子:

SELECT 'vc8'::varchar(8)::varchar
Run Code Online (Sandbox Code Playgroud)

人们可能会期望varchar(没有修饰符),至少我会。但结果是varchar(8)(带修饰符)。下面的小提琴中有许多相关的案例。

2. 数组连接在某些情况下失去了类型修饰符

不需要,所以这在相反的一面出错:

SELECT ARRAY['vc8']::varchar(8)[]
     , ARRAY['vc8']::varchar(8)[] || 'vc8'::varchar(8)
Run Code Online (Sandbox Code Playgroud)

第一个表达式varchar(8)[]按预期产生。
但是第二个,在连接另一个之后varchar(8)被淡化到只是varchar[](没有修饰符)。来自array_append(),下面小提琴中的示例的类似行为。

在大多数情况下,所有这些都无关紧要。Postgres 不会丢失数据,并且当分配给列时,该值无论如何都会被强制转换为正确的类型。然而,相反方向的错误最终会导致一个令人惊讶的异常:

3. 递归 CTE 要求数据类型完全匹配

鉴于此简化表:

CREATE TABLE a (
  vc8  varchar(8)  -- with modifier
, vc   varchar     -- without  
);
INSERT INTO a VALUES  ('a',  'a'), ('bb', 'bb');
Run Code Online (Sandbox Code Playgroud)

虽然此 rCTE 对varchar列有效vc,但对 …

postgresql datatypes cte debugging cast

11
推荐指数
1
解决办法
1825
查看次数

什么规则决定了 SQL Server 何时使用 CTE 作为“优化栅栏”?

不久前,Brent Ozar 发表了一篇文章,详细介绍了 SQL Server 和 PostgreSQL 之间的一些差异:

SQL Server 和 PostgreSQL 的两个重要区别

第一点(“CTE 是优化栅栏”)引起了我的注意,因为很明显,在提供的示例中,SQL Server 将 CTE 和主查询组合在一起并将其优化为单个查询(而不是在PostgreSQL)。

但是,这种行为似乎与我在其他博客和培训课程中看到的示例相反,其中 SQL Server 确实将 CTE 视为优化栅栏,从而可以更好地使用索引、更好的性能等。例如:

更好的选星方式

因此,似乎 SQL Server 有时“尊重”CTE 作为优化栅栏。是否有任何好的资源可以记录 SQL Server 将可靠地将 CTE 作为优化栅栏(或相反的行为)的已知案例的特定列表?

performance sql-server optimization cte query-performance

11
推荐指数
1
解决办法
805
查看次数