游标sql server

Relational database management systems including SQL Server are very good at processing data in sets.

包括SQL Server在内的关系数据库管理系统非常擅长处理集合中的数据。

However, if you want to process data on row-by-row basis rather than in sets, cursors are your only choice. Unfortunately, cursors are extremely slow and so where possible should be replaced with JOINS.

但是,如果要按行而不是按组处理数据,则游标是唯一的选择。 不幸的是,游标非常慢,因此应尽可能用JOINS代替。

为什么光标慢 (Why cursors are slow)

To populate a cursor, database tables are iterated on row-by-row basis rather than in sets. While a cursor is being populated, the table being iterated is locked. Row operations can take a long time to execute depending upon the type of task being performed on each row. While the cursor is open, a table cannot be accessed or updated by the other users. This makes cursor-based operations extremely. Therefore, cursors can be avoided wherever they can by the set based operations.

为了填充游标,数据库表是按行而不是按集合进行迭代的。 当游标被填充时,被迭代的表被锁定。 根据在每一行上执行的任务类型,行操作可能需要很长时间才能执行。 打开游标时,其他用户无法访问或更新表。 这使得基于游标的操作变得极为重要。 因此,可以通过基于集合的操作来尽可能避免游标。

In this article, we will see how cursors process data and look at how and when we can replace cursors with JOINS.

在本文中,我们将看到游标如何处理数据,以及如何以及何时可以用JOINS替换游标。

准备伪数据 (Preparing dummy data)

Let’s start by creating some dummy data to work with.

让我们从创建一些虚拟数据开始。


CREATE Database company;

Next, we need two tables “department” and “employee” with the department table having two columns: id and dep_name, and the employee table having four columns: id, name, dep_id, and salary.

接下来,我们需要两个表“ department”和“ employee”,其中Department表具有两列:id和dep_name,而employee表具有四列:id,name,dep_id和薪水。

The dep_id column of employee table will hold values from the id table of the department table, with a one too many relations between the department and employee tables. Remember this is not a perfectly normalized data table as we just want some data to execute example queries on.

员工表的dep_id列将保存部门表的ID表中的值,部门表与员工表之间的关系过多。 请记住,这不是一个完全标准化的数据表,因为我们只希望一些数据在其上执行示例查询。

USE company;CREATE TABLE department
(id INT PRIMARY KEY,dep_name VARCHAR(50) NOT NULL,)CREATE TABLE employee
(id INT PRIMARY KEY,name VARCHAR(50) NOT NULL,dep_id INT NOT NULL,salary INT NOT NULL

Finally let’s add some dummy data.

最后,让我们添加一些虚拟数据。


USE company;INSERT INTO department VALUES
(1, 'Sales'),
(2, 'HR'),
(3, 'IT'),
(4, 'Marketing'),
(5, 'Finance')INSERT INTO employee VALUES
(1, 'David', 1, 5000),
(2, 'Jim', 2, 6000),
(3, 'Kate', 3, 7500),
(4, 'Will', 4, 6500),
(5, 'Shane', 5, 5500),
(6, 'Shed', 1, 8000),
(7, 'Vik', 2, 7200),
(8, 'Vince', 3, 6600),
(9, 'Jane', 4, 5400),
(10, 'Laura', 5, 6300),
(11, 'Mac', 1, 5700),
(12, 'Pat', 2, 7000),
(13, 'Julie', 3, 7100),
(14, 'Elice', 4, 6800),
(15, 'Wayne', 5, 5000)

一个简单的光标示例 (A simple cursor example)

Let start by creating a simple cursor that will loop through each row in the employee table. There are 15 rows in the employee table and so this won’t illustrate the performance issues that cursors can suffer but if the table contained >10,000 rows the performances issues would be clear.

首先创建一个简单的游标,该游标将遍历employee表中的每一行。 员工表中有15行,因此这不会说明游标可能遇到的性能问题,但是如果表包含的行数大于10,000,则性能问题将很明显。

USE company;DECLARE @EmpId INT
DECLARE @EmpName NVARCHAR(50)DECLARE EmpCursor CURSOR FOR
SELECT id, name FROM employeeOPEN EmpCursorFETCH NEXT FROM EmpCursor INTO @EmpId, @EmpNameWHILE(@@FETCH_STATUS = 0)
BEGINPRINT 'Employee Id = ' + CAST(@EmpId AS NVARCHAR(10)) + ' | Name = ' + @EmpNameFETCH NEXT FROM EmpCursor INTO @EmpId, @EmpName
ENDCLOSE EmpCursor
DEALLOCATE EmpCursor

The above code creates two variables @EmpId and @EmpName that will hold the id and name from the employee table respectively. To declare a cursor we use the DECLARE keyword followed by the cursor’s name and the FOR keyword.

上面的代码创建两个变量@EmpId和@EmpName,它们将分别保存employee表中的ID和名称。 要声明一个游标,我们使用DECLARE关键字,后跟该游标的名称和FOR关键字。

Next, we declare the SELECT statement that will select the records that the cursor will process row-by-row.

接下来,我们声明SELECT语句,该语句将选择游标将逐行处理的记录。

The following lines create EmpCursor that will contain the id and name from the employee table:

以下各行创建EmpCursor,其中将包含employee表中的ID和名称:

DECLARE EmpCursor CURSOR FOR
SELECT id, name FROM employee

At this point, data is added to the cursor. Before we can use the cursor we have to open it using the OPEN keyword. At this point, the cursor points at the top of the table. To move the cursor to the first row we use the FETCH NEXT command. We then use the INTO statement to fetch data from the cursor and insert it into our local variables. To move the cursor to next row we again use FETCH NEXT.

此时,数据已添加到光标。 在使用游标之前,我们必须使用OPEN关键字将其打开。 此时,光标指向表格的顶部。 要将光标移到第一行,我们使用FETCH NEXT命令。 然后,我们使用INTO语句从游标中获取数据并将其插入到我们的局部变量中。 要将光标移到下一行,我们再次使用FETCH NEXT。

A better approach is to use a loop and call FETCH NEXT within the loop so that the cursor can iterate over all the rows. The @@FETCH_STATUS returns 0 as long as there are more rows in a set of records. In the above script, we used a WHILE loop to iterate through all the records in EmpCursor until @@FETCH_STATUS returns 0. When there are no more rows left, the @@FETCH_STATUS returns a non-zero value and the loop will exit. Inside the loop, we print the id and name from each row in the EmpCursor. Finally, we close the cursor we opened using CLOSE keyword.

更好的方法是使用循环并在循环内调用FETCH NEXT,以便游标可以遍历所有行。 只要一组记录中有更多行,@@ FETCH_STATUS将返回0。 在上面的脚本中,我们使用WHILE循环遍历EmpCursor中的所有记录,直到@@ FETCH_STATUS返回0。再没有剩余的行时,@@ FETCH_STATUS返回一个非零值,循环将退出。 在循环内部,我们从EmpCursor中的每一行打印id和名称。 最后,我们使用CLOSE关键字关闭打开的游标。

The output of the above script will be as follows:

上面脚本的输出如下:

涉及多个表的游标示例 (Cursor example involving multiple tables)

In the next example, we will use a cursor that will store the id and dep_id of the employee. Inside the WHILE loop, we will retrieve the department name from the department table where the id of the department matches the dep_id of the employee table.

在下一个示例中,我们将使用一个游标,该游标将存储员工的id和dep_id。 在WHILE循环内,我们将从部门表中检索部门名称,其中部门的ID与employee表的dep_id相匹配。

In addition, if the department name is “Sales”, we will increase the salary of the employee by 10% by multiplying the actual salary by 1.1. If the department name is “HR”, we will increase the salary of the employee by 20% by multiplying the actual salary by 1.2. Finally, if the department name is “IT”, we will increase the salary of the employee by 50% by multiplying the actual salary by 1.5.

此外,如果部门名称为“ Sales”,我们将实际工资乘以1.1将使员工工资增加10%。 如果部门名称为“ HR”,我们将实际工资乘以1.2将使员工工资增加20%。 最后,如果部门名称为“ IT”,我们将实际工资乘以1.5,将员工的工资提高50%。

USE company;DECLARE @Dep_Id INT
DECLARE @Emp_Id INTDECLARE EmpCursor CURSOR FOR
SELECT dep_id, id FROM employeeOPEN EmpCursorFETCH NEXT FROM EmpCursor INTO @Dep_Id, @Emp_IdDECLARE @DepName NVARCHAR(50)
WHILE(@@FETCH_STATUS = 0)
BEGINSELECT @DepName = dep_name FROM department where id = @Dep_IdIF(@DepName = 'Sales')BEGIN UPDATE employee SET salary = (salary * 1.1) WHERE id = @Emp_IdENDELSE IF(@DepName = 'HR')BEGIN UPDATE employee SET salary = (salary * 1.2) WHERE id = @Emp_IdENDIF(@DepName = 'IT')BEGIN UPDATE employee SET salary = (salary * 1.5) WHERE id = @Emp_IdENDFETCH NEXT FROM EmpCursor INTO @Dep_Id, @Emp_Id
ENDCLOSE EmpCursor
DEALLOCATE EmpCursor

Now if you SELECT records from the employee table, you will see an increase of 10%, 20% and 50% on the salaries of employees from Sales, HR and IT department, respectively.

现在,如果您从employee表中选择SELECT记录,您将分别看到Sales,HR和IT部门员工的薪水分别增加10%,20%和50%。

The new salaries are shown in the following table:

下表显示了新的工资:

用JOIN代替游标 (Replacing cursors with JOINs)

As explained earlier, cursors are extremely slow and should be replaced with JOINs whenever possible. Let’s look at how we replace the cursor that we saw in the last example with a JOIN statement. We will use a JOIN statement to increment the salaries of the employees from Sales, HR and IT department by of 10%, 20%, and 50% respectively, as we did in the previous example.

如前所述,游标非常慢,应尽可能用JOIN代替。 让我们看看如何用JOIN语句替换在上一个示例中看到的游标。 与前面的示例一样,我们将使用JOIN语句将销售,人力资源和IT部门员工的薪水分别提高10%,20%和50%。

Note: Before you execute this script, truncate the employee table and insert dummy records as we did at the start of this article. This is to make sure that we achieve the same results using cursors and JOINs.

注意:在执行此脚本之前,请像在本文开头一样,截断employee表并插入虚拟记录。 这是为了确保我们使用游标和JOIN获得相同的结果。

USE company;UPDATE employee
SET salary = CASE WHEN dep_name = 'Sales' THEN (salary * 1.1)WHEN dep_name = 'HR' THEN (salary * 1.2)WHEN dep_name = 'IT' THEN (salary * 1.5)END
FROM employee
JOIN department
ON department.id = employee.dep_id
WHERE (dep_name = 'Sales' OR dep_name = 'HR' OR dep_name = 'IT')

The above query will be at least 20 times faster than the cursor and will achieve same results. We have used a CASE statement to update the salary based on the dep_name. The employee and department tables have been joined so that we can access salary from the employee table and dep_name from department table in the same query. The JOIN has been implemented on the id column of the department table and the dep_id column of the employee table. The WHERE clause is used to filter records where the name of the department is Sales, HR or IT. This is because we are only updating the salaries of employees from these departments.

上面的查询将比游标至少快20倍,并且将获得相同的结果。 我们使用了CASE语句根据dep_name更新工资。 员工表和部门表已连接在一起,因此我们可以在同一查询中访问雇员表中的薪水和部门表中的dep_name。 JOIN已在部门表的id列和雇员表的dep_id列上实现。 WHERE子句用于过滤部门名称为Sales,HR或IT的记录。 这是因为我们仅更新这些部门中员工的薪水。

结论 (Conclusion)

In this article we saw that how cursor based operations lead to performance issues. Therefore, JOINS should always be used in preference to cursors because of their efficiency and simplicity, unless you need to carry out a row-by-row operation on the records.

在本文中,我们看到了基于游标的操作如何导致性能问题。 因此,由于它们的效率和简单性,应该始终优先使用JOINS而不是游标,除非您需要对记录进行逐行操作。

本的其他精彩文章 (Other great articles from Ben)

Sequence Objects in SQL Server
Debugging stored procedures in SQL Server Management Studio (SSMS)
Understanding cursors and replacing them with JOINs in SQL Server
SQL Server中的序列对象
在SQL Server Management Studio(SSMS)中调试存储过程
了解游标并将其替换为SQL Server中的JOIN

翻译自: https://www.sqlshack.com/understanding-cursors-replacing-joins-sql-server/

游标sql server

游标sql server_了解游标并将其替换为SQL Server中的JOIN相关推荐

  1. 在SQL Server中使用JOIN更新表?

    我想更新在其他表上进行联接的表中的列,例如: UPDATE table1 a INNER JOIN table2 b ON a.commonfield = b.[common field] SET a ...

  2. MS SQL SERVER 中merge join合并连接介绍(转)

    1概述 Merge join 合并连接.两个集合进行merge join,需要有一个等值的条件,然后需要两个已排序好的集合. 2 one-to-many与many-to-many 2.1 One-to ...

  3. sql out apply_在SQL Server中CROSS APPLY和OUTER APPLY之间的区别

    sql out apply SQL Server supports table valued functions, what are functions that return data in the ...

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

    游标sql server SQL Server cursors are one common topic on the Internet. You'll find different opinions ...

  5. 游标sql server_使用SQL Server游标–优点和缺点

    游标sql server 介绍 (Intro) In relational databases, operations are made on a set of rows. For example, ...

  6. SQL Server中自定义函数和游标应用的经典案例

    2019独角兽企业重金招聘Python工程师标准>>> SQL Server中自定义函数和游标应用的经典案例 转载于:https://my.oschina.net/zhddzr/bl ...

  7. sql 游标_SQL基础丨游标

    游标 游标是一个重要的概念,提供了一种灵活的操作方式,可以从数据结果集中每次提取一条数据记录进行操作. 在SQL中,游标是一种临时的数据库对象,可以指向存储在数据库中的数据行指针. 示例 -查询her ...

  8. 【Oracle】PL/SQL 显式游标、隐式游标、动态游标

    在PL/SQL块中执行SELECT.INSERT.DELETE和UPDATE语句时,Oracle会在内存中为其分配上下文区(Context Area),即缓冲区.游标是指向该区的一个指针,或是命名一个 ...

  9. Sql Server 中利用游标对table 的数据进行分组统计式输出…

    Sql Server 中利用游标对table 的数据进行分组统计式输出- Table Name: Tb_Color Create table Tb_Color(id int identity(1,1) ...

最新文章

  1. 机器学习简单代码示例
  2. UC,qq浏览器强制横屏
  3. vs实现python c扩展模块
  4. sklearn自学指南(part43)--数据加载工具
  5. 起点海外版 Hybrid App-内嵌页优化实践
  6. Android 应用内实现导航页面,接入百度SDK内置导航,高德SDK内置导航
  7. 论文浅尝 - ICML2020 | 通过关系图上的贝叶斯元学习进行少样本关系提取
  8. 解决WP表前缀更换后出现的You do not have sufficient permission
  9. 网络测试工具 - QCheck
  10. 值传递,指针传递,引用传递
  11. 金字塔型php的9x9乘法口诀表,python中打印金字塔和九九乘法表的几种方法
  12. git-ftp:用git管理ftp服务器简单入门
  13. RT-Thread Studio开发GD32VF103
  14. c语言整人小程序格式,【C语言】整人小程序
  15. 一起欣赏:50+ 极具创意的个人简历设计【下篇】
  16. c语言setw,在C++中,setw(int n)
  17. iOS-设置导航栏颜色(iOS8+)
  18. wlacm 铲雪车snow 题解
  19. ql的python学习之路-day7
  20. ASP.NET压力测试

热门文章

  1. anaconda moviepy_002从零开始学Python—Anaconda下载与安装
  2. 用 rust 写算法
  3. @loj - 2483@「CEOI2017」Building Bridges
  4. tornado的异步效果
  5. 用原生javascript做的一个打地鼠的小游戏
  6. (5)Oracle基础--约束
  7. [转载] 七龙珠第一部——第086话 打进前八强
  8. 当别人问你自定义哪些hooks
  9. php倒计时不停止,php – 当窗口不在焦点时停止的Javascript倒计时器
  10. 见习经理_第一天洗了整天碗