Truyền Tham Số Động Cho Hàm Kiểu Bảng

Vũ Huy Tâm

Khi sử dụng hàm kiểu bảng, một câu hỏi đặt ra là làm thế nào để truyền tham số động cho hàm, trong đó tham số là giá trị của một cột trong bảng? Tình huống này xảy ra khi bạn truy vấn dữ liệu từ một bảng, với mỗi bản ghi bạn cần gọi hàm và tham số được truyền từ một hoặc vài trường nào đó của bản ghi tương ứng. Rõ ràng tham số truyền cho hàm không cố định, mà thay đổi tại mỗi bản ghi.
Ví dụ, tôi lấy hàm đã được đề cập trong bài Hàm Kiểu Bảng, hàm dbo.fnCSVStr2Table để chuyển đổi một chuỗi ký tự phân cách bằng dấu phẩy thành bảng. Nay tôi muốn áp dụng hàm này lên bảng về các model ô tô sau:

CREATE TABLE dbo.Car(Maker VARCHAR(20), ModelList VARCHAR(1000))
 
INSERT INTO dbo.Car(Maker, ModelList)
VALUES('TOYOTA', 'Camry,HighLander,Takoma,Prius,Corolla'),
('HONDA', 'Civic,Accord,CR-V'),
('FORD', 'Escape,Everest,Fusion,Edge')
 
SELECT * FROM dbo.Car

Và mong muốn đầu ra như sau:

Vậy phải làm như thế nào? Cách trực quan nhất có lẽ là dùng một con trỏ (cursor) chạy qua bảng, ở mỗi vòng lặp gọi đến hàm và INSERT kết quả của hàm vào một bảng tạm; đến cuối cùng thì SELECT từ bảng tạm để trả về. Cách làm này chắc hẳn không thể tối ưu. SQL Server cung cấp toán tử APPLY để sử dụng trong trường hợp này:

SELECT c.Maker, b.ValueColumn AS Model
FROM dbo.Car c
CROSS APPLY dbo.fnCSVStr2Table(c.ModelList) b

Cơ chế hoạt động của APPLY tương tự như đoạn mô tả dùng con trỏ ở trên: SELECT từ bảng chính, với mỗi bản ghi gọi đến hàm lấy kết quả ghép chung vào kết quả trả về. Tuy nhiên cơ chế này hoàn toàn dựa vào tập (set-based) nên nhẹ nhàng hơn rất nhiều so với con trỏ. Bạn có thể hình dung APPLY với hàm tương tự như JOIN với bảng: liên kết với bảng thì dùng JOIN, liên kết với hàm thì dùng APPLY. Ở ví dụ trên tôi dùng CROSS APPLY, cư xử của nó giống như INNER JOIN. Khi cần tương tự như OUTER JOIN, bạn dùng OUTER APPLY.


Phiên bản áp dụng: SQL Server 2005 trở lên

Tags: , , ,

6 Comments
Posted on 6/7/2011 | Categories: Function, SQL Server Programming

Các bài viết tương tự

Comments
  • Lê Văn Thương (26/07/2011 12:13 am)

    Tôi đã làm đúng như của bạn
    SELECT c.Maker, b.ValueColumn AS Model
    FROM dbo.Car c
    CROSS APPLY dbo.fnCSVStr2Table(Model) b
    nhưng mà không được.
    Nhưng sau khi chỉnh lại
    SELECT c.Maker, b.ValueColumn AS Model
    FROM dbo.Car c
    CROSS APPLY dbo.fnCSVStr2Table(c.ModelList) b
    thì lại được.

    • Vũ Huy Tâm (27/10/2011 2:47 pm)

      Cám ơn bạn đã chỉ ra, tôi sửa lại rồi. Chắc tại lỗi anh đánh máy ;)

  • Nguyễn Quốc Bảo (19/08/2011 2:13 am)

    Cảm ơn bạn, bài viết này rất hay (đúng ngay cái tôi đang tìm).

  • kinglee (11/01/2012 10:16 pm)

    Tôi nghĩ nên để cái hàm “fnCSVStr2Table” vô trong bài viết này luôn sẽ dễ tham khảo hơn

  • gia vang (24/07/2012 2:19 am)

    tuyệt vời, hihi, đọc mấy cái tiếng anh khó hiểu ghê. Search google ra cái bài này đọc là hiểu ngay cách xài cross apply. Cảm ơn sqlviet

  • Banhe0 (10/07/2015 12:35 am)

    Tại sao mọi người lại ngại dùng cursor nhỉ trong khi nó là cách trực quan nhất để lấy dữ liệu từng ô trong 1 bảng.Có lẽ vì nó chậm?

Leave a Reply

Hướng dẫn: Để nhập mã T-SQL bạn dùng thẻ <pre lang="tsql"> và </pre>.
Ví dụ: <pre lang="tsql">SELECT * FROM MyTable</pre>