如何从单行提取成多行(TSQL)?

2023-10-10 08:08
文章标签 提取 多行 单行 tsql

本文主要是介绍如何从单行提取成多行(TSQL)?,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

如何从单行提取成多行(TSQL)?

2010-05-05 22:39 by 吴秦, 2628 阅读, 12 评论, 收藏, 编辑
Outline:
  • 问题产生背景
  • 交叉连接
  • UNION操作
  • WITH子句
  • 性能比较
  • 抛砖引玉(欢迎指教!)
问题产生背景

从一个仅有数额指标的交易表中创建一个查询,这个查询用于会计日志条目。因此如果一个日志条目应该有存款和借款,但是其他的值应该是相同的,因此我想通过SQL查询从一行提取2行记录。听起来很模糊?我Google之后发现没有任何结果,所以我就写了这篇文章。为了简化,这里举个例子。我有一张表叫做SampleTable,它包含如下记录:

ID

FirstName

LastName

1

Anna

Gates

2

John

Doe

3

Joe

Bloggs

4

Raj

Kumar

现在你的任务是为每个记录创建3行,因此它将显示如下:

ID

FirstName

LastName

ItemNumber

ItemDescription

1

Anna

Gates

1

Item 1

1

Anna

Gates

2

Item 2

1

Anna

Gates

3

Item 3

2

John

Doe

1

Item 1

2

John

Doe

2

Item 2

2

John

Doe

3

Item 3

3

Joe

Bloggs

1

Item 1

3

Joe

Bloggs

2

Item 2

3

Joe

Bloggs

3

Item 3

4

Raj

Kumar

1

Item 1

4

Raj

Kumar

2

Item 2

4

Raj

Kumar

3

Item 3

现在,你如何实现它呢?有许多种方法,但是我们要找出哪个方法查询效率最高。有下面是那种方法:

  • 交叉连接
  • UNION查询
  • WITH查询
1、交叉连接

为了激活大家对交叉连接沉睡的记忆,首先介绍下什么是交叉连接。所谓交叉连接,就是两个表的笛卡尔积的另一称谓。交叉连接为将第一张表的每一行与第二张表的每一行组合产生一新的元组。设两张表R、S分别有k1、k2条记录,每条记录的列数分别为m、n,则交叉连接的结果元组数为k1*k2,每个元组的列数为m+n(前面m列是R的,后面n列是S的)。当然这是在没有where条件的情况下,如果加了where添加可能会过滤掉一部分不符合条件的记录。

所以上面的结果可以看成下面两张表的交叉连接产生的:

image因此可以用如下SQL语句:
交叉连接

本来是打算对表2构建一张临时表,但考虑到SQL Server与Oracle构建临时表是有差异的,考虑到这个我就用上面这种方式(UNION ALL,集合查询)。
2、UNION操作

UNION是集合操作中的一种,SELECT语句的查询结果是元组的集合,所以多个SELECT语句的结果可进行集合操作。集合操作主要包括并操作UNION、交操作INTERSECT和差操作EXCEPT。注意,参加集合操作的各查询结果的列数必须相同;对应项的数据类型也必须相同。

下面是实现代码:

UNION操作

3、WITH子句

WITH AS短语,也叫做子查询部分(subquery factoring),可以让你做很多事情,定义一个SQL片断,该SQL片断会被整个SQL语句所用到。有的时候,是为了让SQL语句的可读性更高些,也有可能是在UNION ALL的不同部分,作为提供数据的部分。特别对于UNION ALL比较有用。因为UNION ALL的每个部分可能相同,但是如果每个部分都去执行一遍的话,则成本太高,所以可以使用WITH AS短语,则只要执行一遍即可。如果WITH AS短语所定义的表名被调用两次以上,则优化器会自动将WITH AS短语所获取的数据放入一个TEMP表里,如果只是被调用一次,则不会。而提示materialize则是强制将WITH AS短语里的数据放入一个全局临时表里。很多查询通过这种方法都可以提高速度。

其实with子句提供定义一个临时视图的方法,这个定义只对with子句出现的那条查询有效。换就话说,就是把查询结果放入一个临时表,然后通过查询语句从临时表查询结果。

如果你对with子句还是不熟悉理解的话,看下面的例子。下面的查询是:查询具有最大余额的账户的语句,如果具有同样最大查询的账户有很多,他们都会被选择。即假设如果最大余额是10000的账户有3个,则这三个账户都会显示出来。 
with max-balance(value) as 
        select max(balance) 
        from account 
select account-number 
from account,max-balance 
where account.balance=max-balance.value 
我想看到这,with子句大家都理解了。下面是上面问题的实现代码:

WITH子句

性能比较

上面三种方法都可以得到相同的结果,那到底它们的性能到底如何呢?下面我们把这三种方法的代码放到同一个查询中执行,如下:
-----------------------------------------
--方法一、交叉连接
-----------------------------------------
SELECT SampleTable.ID, SampleTable.FirstName, SampleTable.LastName, Extender.ItemNumber, Extender.ItemDescription
FROM SampleTable CROSS JOIN
(SELECT 1 AS ItemNumber, 'Item 1' AS ItemDescription
UNION ALL
SELECT 2 AS ItemNumber, 'Item 2' AS ItemDescription
UNION ALL
SELECT 3 AS ItemNumber, 'Item 3' AS ItemDescription) AS Extender;

-----------------------------------------
--方法二、UNION操作
-----------------------------------------
SELECT SampleTable.ID, SampleTable.FirstName, SampleTable.LastName, 1 AS ItemNumber, 'Item 1' AS ItemDescription
FROM SampleTable
UNION ALL
SELECT SampleTable.ID, SampleTable.FirstName, SampleTable.LastName, 2 AS ItemNumber, 'Item 2' AS ItemDescription
FROM SampleTable
UNION ALL
SELECT SampleTable.ID, SampleTable.FirstName, SampleTable.LastName, 3 AS ItemNumber, 'Item 3' AS ItemDescription
FROM SampleTable;

-----------------------------------------
--方法三、WITH子句
-----------------------------------------
WITH ExtendedTable(ID, FirstName, LastName) AS
(SELECT SampleTable.ID, SampleTable.FirstName, SampleTable.LastName
FROM SampleTable)
SELECT *, 1 AS ItemNumber, 'Item 1' AS ItemDescription from ExtendedTable
UNION ALL
SELECT *, 2 AS ItemNumber, 'Item 2' AS ItemDescription from ExtendedTable
UNION ALL
SELECT *, 3 AS ItemNumber, 'Item 3' AS ItemDescription from ExtendedTable

执行查询时选中包括实际的执行计划(在SQL Server的查询菜单下面),得到执行计划如下图所示(由于图太大分三张贴上)。

方法一:

方法一

方法二:

方法二

方法三:

方法三

从图中我们可以清楚地看到方法一得开销仅占15%,而方法二、三相同都占43%。由此可见交叉连接的性能最好,而union操作与with子句性能相对较低。

抛砖引玉

ps.不知道您有没有看到这里,可能很多看官还没看到这节就把网页给关了(⊙﹏⊙b汗)。如果您看到这里了,你能说出造成这个性能差异的原因吗?欢迎大家回帖,包括拍砖。

我指出其中一点:交叉连接和with子句一样都是用构建一张临时表与SampleTable做连接,但是他们的性能差异源于交叉连接时执行select语句时做了聚簇索引。(just maybe,I'am not sure!)

另外推荐一篇讲with ties的文章:偶遇with ties

这篇关于如何从单行提取成多行(TSQL)?的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/179149

相关文章

C#提取PDF表单数据的实现流程

《C#提取PDF表单数据的实现流程》PDF表单是一种常见的数据收集工具,广泛应用于调查问卷、业务合同等场景,凭借出色的跨平台兼容性和标准化特点,PDF表单在各行各业中得到了广泛应用,本文将探讨如何使用... 目录引言使用工具C# 提取多个PDF表单域的数据C# 提取特定PDF表单域的数据引言PDF表单是一

C#实现添加/替换/提取或删除Excel中的图片

《C#实现添加/替换/提取或删除Excel中的图片》在Excel中插入与数据相关的图片,能将关键数据或信息以更直观的方式呈现出来,使文档更加美观,下面我们来看看如何在C#中实现添加/替换/提取或删除E... 在Excandroidel中插入与数据相关的图片,能将关键数据或信息以更直观的方式呈现出来,使文档更

Java后端接口中提取请求头中的Cookie和Token的方法

《Java后端接口中提取请求头中的Cookie和Token的方法》在现代Web开发中,HTTP请求头(Header)是客户端与服务器之间传递信息的重要方式之一,本文将详细介绍如何在Java后端(以Sp... 目录引言1. 背景1.1 什么是 HTTP 请求头?1.2 为什么需要提取请求头?2. 使用 Spr

使用Java解析JSON数据并提取特定字段的实现步骤(以提取mailNo为例)

《使用Java解析JSON数据并提取特定字段的实现步骤(以提取mailNo为例)》在现代软件开发中,处理JSON数据是一项非常常见的任务,无论是从API接口获取数据,还是将数据存储为JSON格式,解析... 目录1. 背景介绍1.1 jsON简介1.2 实际案例2. 准备工作2.1 环境搭建2.1.1 添加

python解析HTML并提取span标签中的文本

《python解析HTML并提取span标签中的文本》在网页开发和数据抓取过程中,我们经常需要从HTML页面中提取信息,尤其是span元素中的文本,span标签是一个行内元素,通常用于包装一小段文本或... 目录一、安装相关依赖二、html 页面结构三、使用 BeautifulSoup javascript

ROS - C++实现RosBag包回放/提取

文章目录 1. 回放原理2. 回放/提取 多个话题3. 回放/提取数据包,并实时发布 1. 回放原理 #include <ros/ros.h>#include <rosbag/bag.h>#include <std_msgs/String.h>int main(int argc, char** argv){// 初始化ROS节点ros::init(argc, argv,

HalconDotNet中的图像特征与提取详解

文章目录 简介一、边缘特征提取二、角点特征提取三、区域特征提取四、纹理特征提取五、形状特征提取 简介   图像特征提取是图像处理中的一个重要步骤,用于从图像中提取有意义的特征,以便进行进一步的分析和处理。HalconDotNet提供了多种图像特征提取方法,每种方法都有其特定的应用场景和优缺点。 一、边缘特征提取   边缘特征提取是图像处理中最基本的特征提取方法之一,通过检

如何根据相同分隔符提取间隔数据?

最近遇到很多提问怎么提取字符的,而这些问题都有一个相同的特征,就是要提取的内容与内容之间,都有着相同的分隔符。当然,这种问题直接用“数据” →  “分列”功能就可以一步到位实现的,但有人喜欢折腾,而更多的人又非得指定函数公式的方法,或者更多的是要保持数据的同步性。   下面,我们就来讲讲用函数公式应该怎么实现这个提取,首先来个数据和要求,如下图,将 - 号间隔的内容依次提取到右边单元格内:

Java8特性:分组、提取字段、去重、过滤、差集、交集

总结下自己使用过的特性 将对象集合根据某个字段分组 //根据id分组Map<String, List<Bean>> newMap = successCf.stream().collect(Collectors.groupingBy(b -> b.getId().trim())); 获取对象集合里面的某个字段的集合 List<Bean> list = new ArrayList<>

OpenCV结构分析与形状描述符(10)检测并提取轮廓函数findContours()的使用

操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C++11 算法描述 在二值图像中查找轮廓。 该函数使用算法 253从二值图像中检索轮廓。轮廓是有用的工具,可用于形状分析和对象检测与识别。参见 OpenCV 示例目录中的 squares.cpp。 findContours 是 OpenCV 库中的一个重要函数