游标sql server_学习SQL:SQL Server游标

游标sql server

SQL Server cursors are one common topic on the Internet. You’ll find different opinions when to use them and when not to do it. Today, we’ll also talk about them and answer the question when (not) to use them.

SQL Server游标是Internet上的一个常见主题。 在何时使用它们以及何时不使用它们时,您会发现不同的意见。 今天,我们还将讨论它们,并回答何时(不)使用它们的问题。

数据模型和总体思路 (The Data Model and the general idea )

In the previous article, Intro to SQL Server loops, we talked about SQL Server loops, but we haven’t used data from the database. That was odd, but that should become much clearer now. Today, while explaining cursors, we’ll use the data from the database to show when (not) to use cursors. The data model we’ll be using is the same one we’re using throughout this series.

在上一篇文章“ SQL Server循环简介”中 ,我们讨论了SQL Server循环,但尚未使用数据库中的数据。 这很奇怪,但是现在应该变得更加清楚了。 今天,在解释游标时,我们将使用数据库中的数据来显示何时(不使用)游标。 在本系列中,我们将使用的数据模型与我们使用的数据模型相同。

SQL Server Cursor - the data model

SQL Server supports 3 different implementations of cursors – Transact-SQL cursors, API cursors, and Client cursors. In this article, we’ll focus on Transact-SQL cursors. You’ll easily recognize them because they are based on the DECLARE CURSOR syntax.

SQL Server支持3种不同的游标实现– Transact-SQL游标,API游标和客户端游标。 在本文中,我们将重点介绍Transact-SQL游标。 您会很容易识别它们,因为它们基于DECLARE CURSOR语法。

SQL Server游标–简介 (SQL Server Cursor – Introduction)

Before we move to code and examples, we should explain what SQL Server cursors are.

在转到代码和示例之前,我们应该解释什么是SQL Server游标。

The SQL Server cursor is T-SQL logic, which allows us to loop through the related query result. This enables us to take the actions sequentially – e.g., perform an update on a single row.

SQL Server游标是T-SQL逻辑,它使我们可以循环浏览相关的查询结果。 这使我们能够按顺序执行操作-例如,在一行上执行更新。

Sometimes this could (seem to) be helpful, but when working with databases, you shouldn’t use procedural programming patterns but rather stick to declarative programming. One of the main reasons is that DBMSs are already optimized to perform actions on sets of data, and therefore you shouldn’t be the one who’s trying to be “smarter than the system”.

有时这可能(似乎)很有帮助,但是在使用数据库时,您不应使用过程编程模式,而应坚持声明式编程。 主要原因之一是DBMS已经过优化,可以对数据集执行操作,因此,您不应成为试图“比系统更智能”的人。

Still, it’s good to know how they work. If nothing else, maybe you’ll meet them in the code you inherit, and you’ll have to rewrite the logic. And before doing anything, you should understand how it works.

尽管如此,还是很高兴知道它们是如何工作的。 如果没有别的,也许您会在继承的代码中遇到它们,并且必须重写逻辑。 在做任何事情之前,您应该了解它是如何工作的。

So, in case you need cursors, this is what you should know about them:

因此,如果您需要游标,那么这是您应该了解的内容:

  • Cursors use variables to store values returned in each part of the loop. Therefore, you’ll need to DECLARE all variables you’ll need

    游标使用变量存储循环的每个部分中返回的值。 因此,您需要对所有需要的变量进行DECLARE
  • The next thing to do is to DECLARE … CURSOR FOR SELECT query, where you’ll declare a cursor and also define the query related to (populating) that cursor

    下一步是DECLARE…CURSOR FOR SELECT查询,您将在其中声明一个游标并定义与(填充)该游标有关的查询
  • You’ll OPEN the cursor and FETCH NEXT from the cursor

    您将打开光标并从光标处获取下一个
  • In the WHILE loop you’ll test the @@FETCH_STATUS variable (WHILE @@FETCH_STATUS = 0). If the condition holds, you’ll enter the loop BEGIN … END block and perform statements inside that block

    在WHILE循环中,您将测试@@ FETCH_STATUS变量(WHILE @@ FETCH_STATUS = 0)。 如果条件成立,您将进入循环BEGIN…END块并在该块内执行语句
  • After you’ve looped through the whole result set, you’ll exit from the loop. You should CLOSE the cursor and DEALLOCATE it. Deallocating is important because this shall delete the cursor definition and free the memory used

    遍历整个结果集之后,您将退出循环。 您应该关闭光标并取消分配它。 取消分配很重要,因为这将删除游标定义并释放所使用的内存

SQL Server游标–示例 (SQL Server Cursor – Examples)

Let’s now take a look at two cursor examples. While they are pretty simple, they nicely explain how cursors work.

现在,让我们看两个光标示例。 尽管它们非常简单,但它们很好地解释了游标的工作方式。

In the first example, we want to get all cities ids and names, together with their related country names. We’ll use the PRINT command to print combinations in each pass of the loop.

在第一个示例中,我们要获取所有城市的ID和名称以及相关的国家名称。 我们将使用PRINT命令在循环的每一遍中打印组合。

-- declare variables used in cursor
DECLARE @city_name VARCHAR(128);
DECLARE @country_name VARCHAR(128);
DECLARE @city_id INT;
 
-- declare cursor
DECLARE cursor_city_country CURSOR FOR
  SELECT city.id, TRIM(city.city_name), TRIM(country.country_name)
  FROM city
  INNER JOIN country ON city.country_id = country.id;
 
-- open cursor
OPEN cursor_city_country;
 
-- loop through a cursor
FETCH NEXT FROM cursor_city_country INTO @city_id, @city_name, @country_name;
WHILE @@FETCH_STATUS = 0
    BEGIN
    PRINT CONCAT('city id: ', @city_id, ' / city name: ', @city_name, ' / country name: ', @country_name);
    FETCH NEXT FROM cursor_city_country INTO @city_id, @city_name, @country_name;
    END;
 
-- close and deallocate cursor
CLOSE cursor_city_country;
DEALLOCATE cursor_city_country;

using SQL cursor example

Using the SQL Server cursor and the while loop returned exactly what we’ve expected – ids and names of all cities, and related countries, we have in the database.

使用SQL Server游标和while循环,可以准确返回我们期望的结果–数据库中所有城市以及相关国家/地区的ID和名称。

The most important thing to mention here is that we could simply return this result set using the original SQL query stored in the DECLARE part of the cursor, so there was no need for a cursor.

这里要提到的最重要的事情是,我们可以使用存储在游标的DECLARE部分中的原始SQL查询简单地返回此结果集,因此不需要游标。

We’ll go with one more example. This time we’ll query the information schema database to return the first 5 tables ordered by table name. While there’s not much sense in using such a query, this example shows you:

我们再举一个例子。 这次,我们将查询信息模式数据库以返回按表名排序的前5个表。 尽管使用这样的查询没有多大意义,但本示例向您展示:

-- declare variables used in cursor
DECLARE @table_name VARCHAR(128);
DECLARE @table_names_5 VARCHAR(128);
 
-- declare cursor
DECLARE cursor_table_names CURSOR FOR
  SELECT TOP 5 TABLE_NAME 
  FROM INFORMATION_SCHEMA.TABLES
  ORDER BY TABLE_NAME ASC;
 
SET @table_names_5 = 'first 5 tables are: '
-- open cursor
OPEN cursor_table_names;
 
-- loop through a cursor
FETCH NEXT FROM cursor_table_names INTO @table_name;
WHILE @@FETCH_STATUS = 0
    BEGIN
    IF @table_names_5 = 'first 5 tables are: '
      SET @table_names_5 = CONCAT(@table_names_5, @table_name)
    ELSE
      SET @table_names_5 = CONCAT(@table_names_5, ', ', @table_name);     
    FETCH NEXT FROM cursor_table_names INTO @table_name;
    END;
PRINT @table_names_5;
 
-- close and deallocate the cursor
CLOSE cursor_table_names;
DEALLOCATE cursor_table_names;

SQL Server Cursor - example with the information schema database

From the coding side, I would like to emphasize that this time, we haven’t printed anything in a loop but rather created a string using CONCAT. Also, we’ve used the IF statement to test if we’re in the first pass, and if so, we haven’t added “,”. Otherwise, we would add “,” to the string.

从编码方面,我想强调一下,这次,我们没有在循环中打印任何内容,而是使用CONCAT创建了一个字符串。 另外,我们已经使用IF语句来测试我们是否处于第一次通过状态,如果是,则没有添加“,”。 否则,我们将在字符串中添加“,”。

After the loop, we’ve printed the result string, closed and deallocated the cursor.

循环之后,我们打印了结果字符串,关闭并重新分配了游标。

We could achieve this using the STRING_AGG function. This one is available starting from the SQL Server 2017 and is the equivalent of MySQL GROUP_CONCAT function.

我们可以使用STRING_AGG函数来实现。 从SQL Server 2017开始提供此功能,等效于MySQL GROUP_CONCAT函数。

SQL Server游标–何时(不)使用它们? (SQL Server Cursor – When (Not) to use them?)

I’ll try to give an objective answer to the question – “When you should use SQL Server cursors and when not”? Since things change during the time and improvements shall be made, either on cursors, either on other objects that “replace” them, take into consideration the date when this article was written. So, let’ start.

我将尝试客观回答这个问题–“什么时候应该使用SQL Server游标,什么时候不使用”? 由于在此期间情况会发生变化,因此应在游标上或在“替换”它们的其他对象上进行改进,请考虑到撰写本文的日期。 所以,让我们开始吧。

You shouldn’t use cursors:

不应该使用游标

You could use cursors:

可以使用游标

  • Mostly for database administration tasks like backups, integrity checks, rebuilding indexes

    主要用于数据库管理任务,例如备份,完整性检查,重建索引
  • For one-time tasks when you’re sure that possible poor performance won’t impact the overall system performance

    对于一次性任务,当您确定可能的不良性能不会影响整体系统性能时
  • Calling a stored procedure a few times using different parameters. In that case, you would get parameters from cursor variables and make calls inside the loop

    使用不同的参数几次调用存储过程 。 在这种情况下,您将从游标变量中获取参数并在循环内进行调用

    Calling a stored procedure or another query inside the cursor (or loop) impacts performance a lot, because, in each step of the cursor loop, you’ll run the query/procedure from the start. If you decide to do that, you should be aware of possible consequences.

    在游标(或循环)内部调用存储过程或其他查询会极大地影响性能,因为在游标循环的每个步骤中,将从头开始运行查询/过程。 如果您决定这样做,则应注意可能的后果。

  • The previous hint brings us to the last bullet when you should use cursors. If you’re completely aware of how they work and you’re pretty sure it won’t impact performance, go for it

    上一个提示将我们带到了您应该使用游标时的最后一个项目符号。 如果您完全了解它们的工作原理,并且确定它不会影响性能,那么请继续努力

SQL Server游标–为什么人们(不)使用它们? (SQL Server Cursor – Why people (don’t) use them?)

The last question I would like to answer is: Why would anyone use a cursor? This is how I see it:

我要回答的最后一个问题是:为什么有人会使用光标? 这是我的看法:

  • People who’re using them for one-time jobs or regular actions where they won’t impact performance have the excuse. One of the reasons is that such code is procedural code, and if you’re used to it, it’s very readable

    将其用于不会影响绩效的一次性工作或定期行动的人会成为借口。 原因之一是这样的代码是过程代码,如果您习惯了它,那么它很可读
  • On the other hand, those who started learning about databases, and are used to procedural programming might use cursors because, as mentioned, they are much closer to procedural programming than to databases. This is not a reason to use them, because the only excuse here would be that you simply don’t know the other (right) way how to get things done

    另一方面,那些开始学习数据库并习惯于过程编程的人可能会使用游标,因为如上所述,它们更接近过程编程而不是数据库。 这不是使用它们的原因,因为这里的唯一借口是您根本不知道另一种(正确的)方法来完成任务
  • The most important thing about cursors is that they are slow when compared to SQL statements, and therefore you should avoid using them because they will sooner or later lead to performance issues (unless you know exactly what you’re doing and why)
  • 关于游标,最重要的是,与SQL语句相比,游标速度较慢 ,因此应避免使用它们,因为游标迟早会导致性能问题(除非您确切地知道自己在做什么以及为什么)

I find it useful that you understand the concept of cursors because there is a great chance, you’ll meet them along the way. They were popular before some new options were added to SQL Server. Also, there is a chance you’ll continue working on a system where somebody before you used them, and you’ll have to continue where they stopped. Maybe you’ll need to replace the cursor (procedural code) with SQL (declarative code).

我发现了解游标的概念很有用,因为很有可能会遇到它们。 在将一些新选项添加​​到SQL Server之前,它们很流行。 同样,您有机会在使用某个人之前继续在一个系统上工作,而您必须在他们停止的地方继续工作。 也许您需要将游标(过程代码)替换为SQL(声明性代码)。

结论 (Conclusion)

There is no better conclusion on cursors, than – don’t use them 🙂 SQL Server implemented a lot of changes that solve problems that were hard to solve using declarative code before. Better spend some time investigating and learning something new, and finally, producing optimal code. Of course, you can use them if you know why you are doing that, and you’re aware of possible problems related to them.

没有比使用游标更好的结论了-不使用游标🙂SQL Server进行了很多更改,以解决以前使用声明性代码难以解决的问题。 最好花一些时间研究和学习新知识,最后生成最佳代码。 当然,如果您知道这样做的原因,并且知道与它们有关的可能问题,则可以使用它们。

目录 (Table of contents)

Learn SQL: CREATE DATABASE & CREATE TABLE Operations
Learn SQL: INSERT INTO TABLE
Learn SQL: Primary Key
Learn SQL: Foreign Key
Learn SQL: SELECT statement
Learn SQL: INNER JOIN vs LEFT JOIN
Learn SQL: SQL Scripts
Learn SQL: Types of relations
Learn SQL: Join multiple tables
Learn SQL: Aggregate Functions
Learn SQL: How to Write a Complex SELECT Query?
Learn SQL: The INFORMATION_SCHEMA Database
Learn SQL: SQL Data Types
Learn SQL: Set Theory
Learn SQL: User-Defined Functions
Learn SQL: User-Defined Stored Procedures
Learn SQL: SQL Views
Learn SQL: SQL Triggers
Learn SQL: Practice SQL Queries
Learn SQL: SQL Query examples
Learn SQL: Create a report manually using SQL queries
Learn SQL: SQL Server date and time functions
Learn SQL: Create SQL Server reports using date and time functions
Learn SQL: SQL Server Pivot Tables
Learn SQL: SQL Server export to Excel
Learn SQL: Intro to SQL Server loops
Learn SQL: SQL Server Cursors
Learn SQL: SQL Best Practices for Deleting and Updating data
Learn SQL: Naming Conventions
学习SQL:CREATE DATABASE&CREATE TABLE操作
学习SQL:插入表
学习SQL:主键
学习SQL:外键
学习SQL:SELECT语句
学习SQL:INNER JOIN与LEFT JOIN
学习SQL:SQL脚本
学习SQL:关系类型
学习SQL:联接多个表
学习SQL:聚合函数
学习SQL:如何编写复杂的SELECT查询?
学习SQL:INFORMATION_SCHEMA数据库
学习SQL:SQL数据类型
学习SQL:集合论
学习SQL:用户定义的函数
学习SQL:用户定义的存储过程
学习SQL:SQL视图
学习SQL:SQL触发器
学习SQL:练习SQL查询
学习SQL:SQL查询示例
学习SQL:使用SQL查询手动创建报告
学习SQL:SQL Server日期和时间函数
学习SQL:使用日期和时间函数创建SQL Server报表
学习SQL:SQL Server数据透视表
学习SQL:将SQL Server导出到Excel
学习SQL:SQL Server循环简介
学习SQL:SQL Server游标
学习SQL:删除和更新数据SQL最佳实践
学习SQL:命名约定

翻译自: https://www.sqlshack.com/learn-sql-sql-server-cursors/

游标sql server

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值