2012-04-30 37 views
11

Tôi đã viết truy vấn này với sự trợ giúp của google để tạo danh sách phân cách từ bảng nhưng tôi không hiểu bất kỳ điều gì từ truy vấn này.Truy vấn này làm gì để tạo danh sách phân tách bằng dấu phẩy SQL Server?

bất cứ ai có thể giải thích cho tôi những gì đang xảy ra

SELECT 
    E1.deptno, 
    allemp = Replace ((SELECT E2.ename AS 'data()' 
         FROM emp AS e2 
         WHERE e1.deptno = e2.DEPTNO 
         FOR xml PATH('')), ' ', ', ') 
FROM EMP AS e1 
GROUP BY DEPTNO; 

Cung cấp cho tôi kết quả

10 CLARK, KING, MILLER 
20 SMITH, JONES, SCOTT, ADAMS, FORD 
30 ALLEN, WARD, MARTIN, BLAKE, TURNER, JAMES 
+1

** XIN LƯU Ý R YOURNG MÃ CỦA BẠN S FA KHÔNG ĐỐI VỚI TEXT CONTAINING CHARACTERS LIKE><& ** bạn sẽ nhận được ký tự mở rộng như '< ',' > '' & 'có cách tốt hơn để thực hiện việc ghép nối này, xem: http://stackoverflow.com/a/5031297/65223 –

Trả lời

35

Cách đơn giản nhất để giải thích là xem xét cách FOR XML PATH hoạt động với XML thực tế.Hãy tưởng tượng một bảng đơn giản Employee:

EmployeeID  Name 
1    John Smith 
2    Jane Doe 

Bạn có thể sử dụng

SELECT EmployeeID, Name 
FROM emp.Employee 
FOR XML PATH ('Employee') 

Điều này sẽ tạo ra XML như sau

<Employee> 
    <EmployeeID>1</EmployeeID> 
    <Name>John Smith</Name> 
</Employee> 
<Employee> 
    <EmployeeID>2</EmployeeID> 
    <Name>Jane Doe</Name> 
</Employee> 

Loại bỏ các 'nhân viên' từ PATH loại bỏ các thẻ xml bên ngoài vì vậy đây truy vấn:

SELECT Name 
FROM Employee 
FOR XML PATH ('') 

có tạo ra

<Name>John Smith</Name> 
    <Name>Jane Doe</Name> 

gì thì bạn đang làm không phải là lý tưởng, 'dữ liệu()' tên cột buộc một lỗi sql vì nó đang cố gắng để tạo ra một thẻ xml mà không phải là một thẻ quy phạm pháp luật, vì vậy lỗi sau được tạo:

Tên cột 'Dữ liệu()' chứa mã định danh XML không hợp lệ theo yêu cầu của FOR XML; '(' (0x0028) là ký tự đầu tiên có lỗi

Các subquery tương quan ẩn lỗi này và chỉ tạo ra XML không có thẻ:.

SELECT Name AS [Data()] 
FROM Employee 
FOR XML PATH ('') 

tạo

John Smith Jane Doe 

Bạn sau đó thay thế khoảng trắng bằng dấu phẩy, khá tự giải thích ...

Nếu tôi là bạn, tôi sẽ điều chỉnh lại truy vấn một chút:

SELECT E1.deptno, 
     STUFF((SELECT ', ' + E2.ename 
       FROM emp AS e2 
       WHERE e1.deptno = e2.DEPTNO 
       FOR XML PATH('') 
      ), 1, 2, '') 
FROM EMP AS e1 
GROUP BY DEPTNO; 

Không có bí danh cột sẽ có nghĩa là không có xml thẻ được tạo ra, và thêm dấu phẩy trong truy vấn chọn có nghĩa là bất kỳ tên với khoảng trống trong sẽ không gây ra lỗi, STUFF sẽ loại bỏ các dấu phẩy đầu tiên và không gian.

PHỤ LỤC

Để xây dựng trên những gì KM đã nói trong một chú thích, vì điều này dường như nhận được một vài quan điểm hơn, cách chính xác để thoát khỏi nhân vật XML sẽ được sử dụng .value như sau:

SELECT E1.deptno, 
     STUFF((SELECT ', ' + E2.ename 
       FROM emp AS e2 
       WHERE e1.deptno = e2.DEPTNO 
       FOR XML PATH(''), TYPE 
      ).value('.', 'NVARCHAR(MAX)'), 1, 2, '') 
FROM EMP AS e1 
GROUP BY DEPTNO; 
+1

Giải thích rất tốt cảm ơn bạn! –

+3

+1 lời giải thích tốt, nhưng ** XIN LƯU Ý MÃ NÀY S FA KHÔNG DÀNH CHO TEXT CONTAINING CHARACTERS LIKE><& ** bạn sẽ nhận được ký tự mở rộng như '< ',' > '' & 'có một cách tốt hơn để làm việc ghép nối này , xem: http://stackoverflow.com/a/5031297/65223 –

+1

Bạn đang thiếu một trích dẫn duy nhất để kết thúc '' NVARCHAR (MAX) '' – wilsjd

0

Truy vấn bên ngoài lấy một danh sách các số phận, và sau đó là subquery được chạy cho mỗi số phận trả lại tất cả tên của bộ phận đó. Truy vấn con sử dụng câu lệnh FOR XML để định dạng đầu ra thành một danh sách được phân cách bằng dấu phẩy đơn lẻ.

6

Tháo rời từng bước một - từ trong ra ngoài.

Bước 1:

Chạy truy vấn trong cùng và xem những gì nó tạo ra:

SELECT E2.ename AS 'data()' 
FROM emp AS e2 
WHERE e2.DEPTNO = 10 
FOR XML PATH('') 

Bạn sẽ nhận được một cái gì đó đầu ra như:

CLARK KING MILLER 

Bước 2:

Các REPLACE chỉ thay thế không gian với , - vì thế quay đầu ra của bạn vào

CLARK, KING, MILLER 

Bước 3:

Truy vấn bên ngoài được giá trị deptno - cộng với các kết quả từ các truy vấn bên trong - và sản xuất cuối cùng của bạn kết quả.

1

Máy chủ SQL 2017 giúp việc này trở nên dễ dàng hơn nhiều với new STRING_AGG. Gần đây đã xem qua bài đăng này và chuyển sang chiến lược STUFF/FOR XML của mình để sử dụng chức năng chuỗi mới. Cũng tránh cần phải thực hiện thêm JOIN/SUBQUERY và phí trên FOR XML (và các vấn đề mã hóa lẻ) và khó diễn giải SQL.

SELECT E1.deptno, 
     STRING_AGG(E1.ename, ', ') AS allemp 
FROM EMP AS e1 
GROUP BY DEPTNO; 

Note: Cũng hãy chắc chắn check out the counterpart STRING_SPLIT để làm việc với dữ liệu SQL phân định dễ dàng hơn nhiều.

Các vấn đề liên quan