SQL:如何遍历 SELECT 语句的结果?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3901601/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-01 07:52:18  来源:igfitidea点击:

SQL: How do I loop through the results of a SELECT statement?

sqlloopsfor-loopticket-system

提问by Justin

How do I loop through the results of a SELECT statement in SQL? My SELECT statement will return just 1 column but n results.

如何循环遍历 SQL 中 SELECT 语句的结果?我的 SELECT 语句将只返回 1 列但返回 n 个结果。

I have created a fictional scenario below complete with the Pseudo code of what I'm trying to do.

我在下面创建了一个虚构的场景,其中包含我正在尝试做的伪代码。

Scenario:

设想:

Students are registering for their classes. They submit a form with multiple course selections (ie. select 3 different courses at once). When they submit their registration I need to ensure there is still room left int the courses they have selected (note I will do a similar check before presenting them with course selection UI but I need to verify afterwards in case somebody else has gone in and swipped up the remaining spots).

学生正在注册他们的课程。他们提交包含多个课程选择的表格(即一次选择 3 门不同的课程)。当他们提交注册时,我需要确保他们选择的课程仍有剩余空间(注意,在向他们展示课程选择 UI 之前,我会做类似的检查,但我需要事后验证,以防其他人进入并刷卡剩下的地方)。

Pseudo Code:

伪代码:

DECLARE @StudentId = 1
DECLARE @Capacity = 20

-- Classes will be the result of a Select statement which returns a list of ints
@Classes = SELECT classId FROM Student.CourseSelections
WHERE Student.CourseSelections = @StudentId

BEGIN TRANSACTION
DECLARE @ClassId int
foreach (@classId in @Classes)
{
   SET @SeatsTaken = fnSeatsTaken @classId

   if (@SeatsTaken > @Capacity)
   {
       ROLLBACK;  -- I'll revert all their selections up to this point
       RETURN -1;
   }
   else
   {
       -- set some flag so that this student is confirmed for the class
   }
}

COMMIT
RETURN 0

My real problem is a similar "ticketing" problem. So if this approach seems very wrong please feel free to recommend something more practical.

我真正的问题是类似的“票务”问题。因此,如果这种方法看起来非常错误,请随时推荐更实用的方法。

EDIT:

编辑:

Attempting to implement the solution below. At this point it doesn't work. Always returns "reserved".

尝试实施以下解决方案。此时它不起作用。总是返回“保留”。

DECLARE @Students TABLE
(
 StudentId int
,StudentName nvarchar(max)
)

INSERT INTO @Students
 (StudentId ,StudentName)
VALUES
 (1, 'John Smith')
 ,(2, 'Jane Doe')
 ,(3, 'Hyman Johnson')
 ,(4, 'Billy Preston')

-- Courses
DECLARE @Courses TABLE
(
 CourseId int
,Capacity int
,CourseName nvarchar(max)
)

INSERT INTO @Courses
 (CourseId, Capacity, CourseName)
VALUES
 (1, 2, 'English Literature'),
 (2, 10, 'Physical Education'),
 (3, 2, 'Photography')


-- Linking Table
DECLARE @Courses_Students TABLE
(
 Course_Student_Id int
,CourseId int
,StudentId int
)

INSERT INTO @Courses_Students
 (Course_Student_Id, StudentId, CourseId)
VALUES
 (1, 1, 1),
 (2, 1, 3),
 (3, 2, 1),
 (4, 2, 2),
 (5, 3, 2),
 (6, 4, 1),
 (7, 4, 2)

SELECT Students.StudentName, Courses.CourseName FROM @Students Students INNER JOIN
@Courses_Students Courses_Students ON Courses_Students.StudentId = Students.StudentId INNER JOIN
@Courses Courses ON Courses.CourseId = Courses_Students.CourseId

DECLARE @StudentId int = 4

-- Ideally the Capacity would be database driven
-- ie. come from the Courses.Capcity.
-- But I didn't want to complicate the HAVING statement since it doesn't seem to work already.
DECLARE @Capacity int = 1 

IF EXISTS (Select *
 FROM
  @Courses Courses INNER JOIN
  @Courses_Students Courses_Students ON Courses_Students.CourseId = Courses.CourseId
 WHERE
  Courses_Students.StudentId = @StudentId
 GROUP BY
  Courses.CourseId
 HAVING
  COUNT(*) > @Capacity)
BEGIN
 SELECT 'full' as Status
END
ELSE BEGIN
 SELECT 'reserved' as Status
END

回答by gbn

No loop needed. You're looking at a standard aggregate with COUNT and GROUP.

不需要循环。您正在查看带有 COUNT 和 GROUP 的标准聚合。

Of course, some details are needed but the principle is this...

当然,一些细节是需要的,但原理是这样的......

DECLARE @StudentId = 1
DECLARE @Capacity = 20

-- Classes will be the result of a Select statement which returns a list of ints
IF EXISTS (SELECT *
    FROM
        Student.CourseSelections CS
        JOIN
        ---this is where you find out course allocations somehow
        ClassTable C ON CS.classId = C.classId 
    WHERE
        Student.CourseSelections = @StudentId
    GROUP BY  --change this, it depends on where you find out course allocations
        ClassID
    HAVING
        COUNT(*) > @Capacity)
   'no'
ELSE
   'yes'

Edit:

编辑:

I've changed the link table. Course_Student_ID is usually not needed in link tables.

我已经更改了链接表。链接表中通常不需要 Course_Student_ID。

The JOIN now

现在加入

  • gets the courses for that student
  • then looks at all students on this course and compares to capacity
  • 获取该学生的课程
  • 然后查看本课程的所有学生并与容量进行比较

Cut down version of above:

上面的删减版:

...
-- Linking Table
DECLARE @Courses_Students TABLE (
,CourseId int
,StudentId int)

INSERT INTO @Courses_Students
 (StudentId, CourseId)
VALUES (1, 1), (1, 3), (2, 1), (2, 2), (3, 2), (4, 1), (4, 2)

DECLARE @StudentId int = 4

--straight list
SELECT
     C.CourseName, C.Capacity, COUNT(*)
 FROM
  @Courses_Students CSThis
  JOIN
  @Courses C ON CSThis.CourseId = C.CourseId
  JOIN
  @Courses_Students CSOthers ON CSOthers.CourseId = C.CourseId
 WHERE
  CSThis.StudentId = @StudentId
 GROUP BY
  C.CourseName, C.Capacity

--oversubscribed list
  SELECT
     C.CourseName, C.Capacity, COUNT(*)
 FROM
  @Courses_Students CSThis
  JOIN
  @Courses C ON CSThis.CourseId = C.CourseId
  JOIN
  @Courses_Students CSOthers ON CSOthers.CourseId = C.CourseId
 WHERE
  CSThis.StudentId = @StudentId
 GROUP BY
  C.CourseName, C.Capacity
  HAVING
      COUNT(*) > C.Capacity

回答by Daniel Mo?mondor

Avoid looping through result sets in SQL as much as you can. If you really can't (if you really are a standard programmer but profession leads you into SQL) use cursors. They don't smell nice, but are unavoidable at times.

尽可能避免在 SQL 中遍历结果集。如果你真的不能(如果你真的是一个标准的程序员,但专业使你进入 SQL)使用cursors。它们闻起来并不好闻,但有时是不可避免的。

回答by codingbadger

Another option would be to implement a CHECK Constrainton your table that contains the Course information. The check constraint could call your existing function to check that there are free seats.

另一种选择是在包含课程信息的表上实施CHECK 约束。检查约束可以调用您现有的函数来检查是否有空闲座位。

Wrap all of your Inserts/Updates in to one transaction. If any of the Inserts/Updates fails then the entire transaction will be rolled back.

将您的所有插入/更新包装到一个事务中。如果任何插入/更新失败,则整个事务将被回滚。