3

SQL で SELECT ステートメントの結果をループするにはどうすればよいですか? 私の SELECT ステートメントは 1 列だけを返しますが、結果は n です。

私がやろうとしていることの疑似コードを完全に備えた、以下の架空のシナリオを作成しました。

シナリオ:

学生は自分のクラスに登録しています。複数のコースを選択したフォームを送信します (つまり、一度に 3 つの異なるコースを選択します)。彼らが登録を送信するとき、彼らが選択したコースにまだ余裕があることを確認する必要があります(コース選択UIを提示する前に同様のチェックを行いますが、他の誰かが参加してスワイプした場合に備えて後で確認する必要があることに注意してください)残りのスポットをアップします)。

疑似コード:

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

私の本当の問題は、同様の「発券」の問題です。したがって、このアプローチが非常に間違っていると思われる場合は、より実用的な方法をお勧めしてください。

編集:

以下の解決策を実装しようとしています。この時点では機能しません。常に「予約済み」を返します。

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

INSERT INTO @Students
 (StudentId ,StudentName)
VALUES
 (1, 'John Smith')
 ,(2, 'Jane Doe')
 ,(3, 'Jack 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
4

3 に答える 3

6

ループは必要ありません。COUNTとGROUPを含む標準の集計を表示しています。

もちろん、いくつかの詳細が必要ですが、原則はこれです...

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'

編集:

リンクテーブルを変更しました。通常、Course_Student_IDはリンクテーブルでは必要ありません。

今すぐ参加

  • その学生のためのコースを取得します
  • 次に、このコースのすべての学生を見て、定員と比較します

上記のバージョンを削減:

...
-- 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
于 2010-10-10T18:49:26.300 に答える
0

SQLの結果セットをできるだけループしないようにします。あなたが本当にできない場合(あなたが本当に標準的なプログラマーであるが、職業があなたをSQLに導く場合)、カーソルを使用してください。いい匂いはしませんが、やむを得ない場合があります。

于 2010-10-10T18:48:09.140 に答える
0

別のオプションは、コース情報を含むテーブルにCHECK 制約を実装することです。チェック制約は、既存の関数を呼び出して、空席があることを確認できます。

すべての挿入/更新を 1 つのトランザクションにラップします。挿入/更新のいずれかが失敗すると、トランザクション全体がロールバックされます。

于 2010-10-10T19:37:38.807 に答える