Do hôm nay rảnh rỗi, có 1 bạn chat trên Yahoo nhờ mình truy vấn 1 bài toán SQL dưới đây, thấy bài này khá hay nên mình tiện post bài lên đây hy vọng sẽ giúp ích 1 ai đó nếu gặp trường hợp tương tự.
ĐỀ BÀI: Cho 3 bảng CSDL có cấu trúc như hình bên dưới
Hãy dùng câu lệnh truy vấn trong SQL để có kết quả như sau:
LỜI GIẢI:
Sau khi xây dựng cấu trúc trên MS SQL ta có các bảng và cấu trúc dữ liệu như sau :
Đối với đề bài như trên ta có thể có 2 cách để truy vấn câu lệnh SQL
1. Với số môn fix cứng 2 cột
Ở đây ta lấy VD 2 cột lấy ra chỉ có điểm của môn Toán và môn Văn. Vậy kết quả sau khi truy vấn sẽ là
STEP 1: Sử dụng từ khóa JOIN để kết nối các bản ghi với nhau từ 3 bảng CSDL và tạo ra 1 bảng tạm trong DB
Dữ liệu trong bảng tạm sẽ hiển thị như hình bên dưới:
STEP 2: Sử dụng từ khóa UNION ALL để kết hợp 2 bản ghi có cùng cấu trúc như sau
Kết quả cuối cùng ==>
2. Đối với các môn không fix cứng (Có bao nhiêu môn thì sẽ hiển thị từng đấy cột)
Câu truy vấn phải trả về kết quả như sau:
SQL query
Nếu ai làm được trường hợp 2 truy vấn ngắn hơn thì post lên cho mọi người tham khảo nhé :)
ĐỀ BÀI: Cho 3 bảng CSDL có cấu trúc như hình bên dưới
Hãy dùng câu lệnh truy vấn trong SQL để có kết quả như sau:
LỜI GIẢI:
Sau khi xây dựng cấu trúc trên MS SQL ta có các bảng và cấu trúc dữ liệu như sau :
Đối với đề bài như trên ta có thể có 2 cách để truy vấn câu lệnh SQL
1. Với số môn fix cứng 2 cột
Ở đây ta lấy VD 2 cột lấy ra chỉ có điểm của môn Toán và môn Văn. Vậy kết quả sau khi truy vấn sẽ là
Họ Tên HS | Môn học 1 | Môn học 2 | Học lực | Hạnh kiểm
STEP 1: Sử dụng từ khóa JOIN để kết nối các bản ghi với nhau từ 3 bảng CSDL và tạo ra 1 bảng tạm trong DB
select * into TestTmp from( select s.fullname, sub.subjectName, m.mark, s.Aptitude, s.Conduct from TBL_Subject sub join TBL_Mark m on sub.subjectID = m.subjectID join TBL_Student s on s.studentID = m.studentID) as a
Dữ liệu trong bảng tạm sẽ hiển thị như hình bên dưới:
STEP 2: Sử dụng từ khóa UNION ALL để kết hợp 2 bản ghi có cùng cấu trúc như sau
select fullname, sum(Toan) as Toan, sum(Van) as Van, Aptitude, Conduct
from
(
select fullname, mark as 'Toan', 0 as 'Van', Aptitude, Conduct
from TestTmp
where subjectName=N'toán'
union all
select fullname, 0 as 'Toan', mark as 'Van', Aptitude, Conduct
from TestTmp
where subjectName=N'Văn'
) as a
group by fullname, Aptitude, Conduct
Kết quả cuối cùng ==>
2. Đối với các môn không fix cứng (Có bao nhiêu môn thì sẽ hiển thị từng đấy cột)
Câu truy vấn phải trả về kết quả như sau:
Họ tên | Môn học 1 | Môn học 2 | .....| Môn học n | Học lực | Hạnh kiểm
SQL query
----Kiểm tra nếu tồn tại bảng #TestTmp thì xóa đi và tạo lại bảng mới
IF EXISTS (select * from dbo.sysobjects where id = object_id(N'TestTmp'))
drop table TestTmp
select * into TestTmp
from
(
select s.fullname, sub.subjectName, m.mark, s.Aptitude, s.Conduct
from TBL_Subject sub
join TBL_Mark m
on sub.subjectID=m.subjectID
join TBL_Student s
on s.studentID=m.studentID
) as a
GO
----- Thưc hiện truy vấn nâng cao
DECLARE @STR_UNION varchar(20)set @STR_UNION = 'union all'
DECLARE @SQLSUM nvarchar(max)
DECLARE @SQL nvarchar(max) set @SQL = ''
DECLARE @STRSBJ nvarchar(max) set @STRSBJ = ''
DECLARE @STRSBJ_TMP nvarchar(max) set @STRSBJ_TMP = ''
DECLARE @subjectName nvarchar(50)
DECLARE ListSubject CURSOR FOR select distinct subjectName from TBL_Subject
OPEN ListSubject
FETCH NEXT FROM ListSubject INTO @subjectName
WHILE @@FETCH_STATUS = 0
BEGIN
BEGIN TRY
--
set @SQLSUM = ''
DECLARE @subjectNameChild nvarchar(50)
DECLARE ListSubjectChild CURSOR FOR select distinct subjectName from TBL_Subject
OPEN ListSubjectChild
FETCH NEXT FROM ListSubjectChild INTO @subjectNameChild
WHILE @@FETCH_STATUS = 0
BEGIN
BEGIN TRY
if(@subjectNameChild=@subjectName)
set @STRSBJ_TMP = @STRSBJ_TMP + 'mark as '+@subjectNameChild+','
else
set @STRSBJ_TMP = @STRSBJ_TMP + '0 as '+@subjectNameChild+','
set @SQLSUM = @SQLSUM + 'sum('+@subjectNameChild+') as '+@subjectNameChild+','
END TRY
BEGIN CATCH
END CATCH
FETCH NEXT FROM ListSubjectChild INTO @subjectNameChild
END
CLOSE ListSubjectChild
DEALLOCATE ListSubjectChild
set @STRSBJ = @STRSBJ_TMP
set @STRSBJ_TMP=''
--
set @SQL = @SQL + 'select fullname, '+@STRSBJ+' Aptitude, Conduct from
TestTmp
where subjectName=N'''+@subjectName+''''
set @SQL = @SQL + CHAR(13) + CHAR(10)
set @SQL = @SQL + @STR_UNION
set @SQL = @SQL + CHAR(13) + CHAR(10)
END TRY
BEGIN CATCH
END CATCH
FETCH NEXT FROM ListSubject INTO @subjectName
END
CLOSE ListSubject
DEALLOCATE ListSubject
--print @SQLSUM
set @SQL = substring(@SQL, 0, len(@SQL)-(len(@STR_UNION)+1))
set @SQL = 'select fullname, '+@SQLSUM+' Aptitude, Conduct
from(' + CHAR(13) + CHAR(10)+ @SQL + CHAR(13) + CHAR(10) + ') as a' + CHAR(13) + CHAR(10)
+'group by fullname, Aptitude, Conduct'
exec(@SQL)
Kết quả cuối cùng trả về
Nếu ai làm được trường hợp 2 truy vấn ngắn hơn thì post lên cho mọi người tham khảo nhé :)















Tôi có thể làm được ngắn hơn, liên hệ với tôi nhá.
ReplyDeleteNhá gì mà nhá, chú cò vào đây nghịch ah :)) Ngắn hơn thì post bài ở dưới đi
ReplyDeletevẫn còn lỗi bạn ơi? giả sử tên môn học có 2 từ trở lên thì vẫn lỗi
ReplyDeleteCác anh có thể chú thích thêm về từng đoạn code được ko ạ ?
ReplyDeleteVà có cách nào ngắn hơn mà dễ hiểu hơn thì post lên e tham khảo với
Em thấy sử dụng câu lệnh Pivot của SQL2005 trở lên rất nhanh và gọn với thuật toán này :D
ReplyDeletebài toán này đc giải từ thời còn xài SQL 2000, mà bạn này bảo có cách giải ngắn hơn thì post lên cho mọi người tham khảo.
DeleteMình dùng Toad for Oracle ko có PIVOT. có cách nào nhanh hơn hem. code này dài quá trời o.O
ReplyDeleteTrường hợp ngược lại với ví dụ trên thì làm ntn các bạn? (dữ liệu là cột, muốn quay về thành hàng thì ntn), bạn nào biết post lên cho mình tham khảo với?
ReplyDelete