2011-10-19 76 views
9

Tôi đang làm việc trên một truy vấn SQL bằng cách sử dụng pvots với cột động trong SQL Server (T-sql). Thay vì gửi truy vấn dài dòng của tôi, tôi sẽ minh họa sự cố của tôi với mô hình được đơn giản hóa.Pivots với cột động trong SQL Server

tôi tạo 2 bảng: Table1 và Table2 và cư chúng với một vài mục như sau:

Table1:


Col_ID1 ............ ... Col_Name

1 ......................... Jan-11

2 ........ ................. Feb-11

3 ......................... Mar-11

Table2:


Col_ID2 ...... Tài khoản ..... Tên tài khoản ...... Số tiền

1 ............... 121 ......... ..Electricity ............ 10000

2 ............... 121 ........... Điện. ........... 20000

3 ............... 121 ........... Điện ............ 30000

1 ............... 122 ........... Điện thoại .............. 100

2. .............. 122 ........... Điện thoại .............. 200

3 ... ............ 122 ........... Điện thoại .............. 300

Tôi đang tạo Pivot, nhưng tôi muốn các tên cột được tạo theo tham số (dựa trên các ngày được nhập từ các đầu vào s creen) và không được mã hóa cứng.

Các truy vấn dưới đây hoạt động tốt, nhưng chỉ đưa ra một vài cột như foll:

Jan-11 ........... Feb-11 ........ ... Mar-11

10,000.00 20,000.00 ...... ...... 30,000.00

100,00 ............... 200.00 .... ....... 300.00

Tôi cũng muốn truy vấn trả lại các cột mô tả, như foll:

Tài khoản ........... Tên tài khoản ........... Jan-11 ............ Feb-11 .. ............ Mar-11

121 ................. Điện ............ ...... 10.000.00 ...... 20.000.00 .......... 30,000.00

122 ................. Điện thoại .. ................... 100.00 ........... 200.00 ............. 300.00

Ai đó có thể giúp tôi sửa đổi truy vấn để đạt được mục tiêu không?

Truy vấn này là một sự thích nghi của các bài viết sau đây được viết bởi Tiến sĩ Andras trong Tháng Chín 2007. http://www.simple-talk.com/community/blogs/andras/archive/2007/09/14/37265.aspx

Có người nhận xét rằng các mã có thể phải chịu tấn công Injection và đề xuất sử dụng Quotename chức năng thay vì concatenating dấu ngoặc vuông .

Bạn có thể giải thích cách sử dụng Quotename trong truy vấn của tôi không.

Cảm ơn rất nhiều,

Leon Lai .
.
.

Dưới đây là truy vấn của tôi:

------------------------ tạo & populate table1 ---------- ----------------------

CREATE TABLE Table1 
(Col_ID1 INT, 
Col_Name varchar(10)) 

INSERT INTO Table1 VALUES (1, 'Jan-11') 
INSERT INTO Table1 VALUES (2, 'Feb-11') 
INSERT INTO Table1 VALUES (3, 'Mar-11') 

--------------------- ---- tạo & populate table2 ----------------------------------

CREATE TABLE Table2 
(Col_ID2 INT, 
Account varchar(10), 
AccountName varchar(20), 
Amount numeric(18,6)) 

INSERT INTO Table2 VALUES (1, 121, 'Electricity', 10000) 
INSERT INTO Table2 VALUES (2, 121, 'Electricity', 20000) 
INSERT INTO Table2 VALUES (3, 121, 'Electricity', 30000) 
INSERT INTO Table2 VALUES (1, 122, 'Telephone', 100)   
INSERT INTO Table2 VALUES (2, 122, 'Telephone', 200) 
INSERT INTO Table2 VALUES (3, 122, 'Telephone', 300) 

- --------------------------------- tạo các tiêu đề cột -------------- -----

DECLARE @cols NVARCHAR(2000) 
SELECT @cols = STUFF((SELECT DISTINCT TOP 100 PERCENT 
'],[' + t2.Col_Name 
FROM Table1 AS t2 
ORDER BY '],[' + t2.Col_Name 
FOR XML PATH('') 
), 1, 2, '') + ']' 

------------------------------------- create @query --- -------------------

DECLARE @query NVARCHAR(4000) 

SET @query = N'SELECT '+ 
@cols +' 

FROM 

------------------------ --subquery -----

(SELECT
t1.Col_Name,
t2.Account,
t2.Amount
FROM Table1 AS t1
JOIN Table2 AS t2 ON t1.Col_ID1 = t2.Col_ID2
) p

-------------------- trục ------------ -------------

PIVOT
(
Sum ([Amount])
FOR Col_Name IN
('+
@cols +')
) AS pvt '

---------------------- exec & thả ----------

EXECUTE(@query) 
drop table table1 
drop table table2 

=== ================================================== ==

Hi Philip,

Cảm ơn bạn đã trả lời.

Truy vấn được đề xuất của bạn hoạt động suôn sẻ và tạo màn hình dự kiến, nhưng nó không chính xác như những gì tôi muốn.

Đầu tiên, cảm ơn vì các mã: CHỌN @cols = ISNULL (@cols + '', '') + '[' + COL_NAME + ']'

Nó là đơn giản và không thay thế dòng của tôi liên quan đến công cụ và đường dẫn xml, dường như có cùng tác dụng.

Hãy để tôi giải thích những gì tôi muốn làm.

Tôi muốn phát triển truy vấn trong Sap Business 1 (Gói kế toán - hoặc gọi nó là ERP). Sap sử dụng T-sql trong Microsoft Server 2008 và có trình tạo truy vấn riêng. Với rất ít ngoại lệ, Sap sql tương tự như T-sql.

Tôi muốn truy vấn của mình cung cấp danh sách tất cả thu nhập và chi phí theo tháng trong khoảng thời gian 12 tháng.

Tuy nhiên, tôi không muốn tiêu đề cột của tôi để được hardcoded, (vì điều này sẽ yêu cầu tôi sửa đổi truy vấn của tôi bất cứ lúc nào) như sau:

Jan-11, Feb-11, Mar- 11, Apr-11, ..... Dec-11

Thay vào đó, tôi muốn các tiêu đề cột được tạo động từ những ngày mà người dùng nhập vào màn hình nhập.

Như tôi đã đề cập, truy vấn mà tôi đăng trên diễn đàn là phiên bản đơn giản của truy vấn thực, chỉ được sử dụng để minh họa. Truy vấn thực có chứa một số biến và một màn hình đầu vào (gọi là Truy vấn - Tiêu chí lựa chọn hộp trong Sap b1) cho phép người dùng nhập một ngày. Đó là ngày này sẽ được sử dụng để xác định tên cột động.

Đây là lý do tại sao tôi cần các công cụ phức tạp như @cols, @query, trục, vv

Nếu tôi đầu vào, nói '01 .06.11' (ngày 01 tháng 6 năm 2011) trong màn hình đầu vào, ngày này sẽ được được chuyển đến sql để xác định tên của các tiêu đề cột như foll:

Jun-11, Jul-11, Aug-11 ..... May-12.

Nếu tôi đầu vào một ngày khác, nói '01 .09.10' (ngày 01 tháng chín năm 2010), các tiêu đề cột sẽ thay đổi để:

Sep-10, Oct-10, .... Aug-11

Dường như bạn đã mã hóa cứng các tiêu đề cột của tôi.

Bạn có thể xem xét lại truy vấn của mình và đề xuất thứ gì đó sẽ cho phép tên cột được tạo tham số thay vì mã hóa cứng không?

Cảm ơn

Leon Lai

Trả lời

11

Thêm các cột là rất đơn giản. Các truy vấn cuối cùng sẽ là

SELECT Account, AccountName, [Feb-11],[Jan-11],[Mar-11] FROM 
(SELECT 
t1.Col_Name, 
t2.Account, 
t2.AccountName, 
t2.Amount 
FROM Table1 AS t1 
JOIN Table2 AS t2 ON t1.Col_ID1 = t2.Col_ID2 
) p 
PIVOT 
(
Sum ([Amount]) 
FOR Col_Name IN 
([Feb-11],[Jan-11],[Mar-11]) 
) AS pvt 

trong đó có t2.AccountName thêm vào subquery, và Tài khoản và AccountName thêm vào SELECT ban đầu. Quăng chúng vào báo cáo kết quả xây dựng và bạn đã hoàn tất:

DECLARE @query NVARCHAR(4000) 
SET @query = N'SELECT Account, AccountName, ' + @cols +' FROM 

(SELECT 
t1.Col_Name, 
t2.Account, 
t2.AccountName, 
t2.Amount 
FROM Table1 AS t1 
JOIN Table2 AS t2 ON t1.Col_ID1 = t2.Col_ID2 
) p 

PIVOT 
(
Sum ([Amount]) 
FOR Col_Name IN 
('+ 
@cols +') 
) AS pvt ' 

Đối với SQL injection, cách duy nhất tôi có thể thấy rằng xảy ra là nếu một người nào đó bằng cách nào đó nhúng mã độc hại trong Table1.Col_Name, và nếu bạn phải lo lắng về điều đó, bạn có vấn đề lớn hơn "khóa xuống" truy vấn động này.

Cũng đáng nói đến, tôi sử dụng phần sau để xây dựng danh sách các cột (@Cols) vì nó ngắn hơn và dễ đọc hơn, nhưng chủ yếu là vì tôi không thích XML.

DECLARE @cols NVARCHAR(2000)  
SELECT @cols = isnull(@cols + ',', '') + '[' + Col_Name + ']' 
FROM Table1 
ORDER BY Col_Name 
+0

Xin chào Philip, tôi đã đăng câu hỏi trả lời của bạn Cảm ơn Leon –

+0

Đây là câu trả lời rất hay! – jTC

+0

Sử dụng 'quotename (Col_Name)' thay vì ''[' + Col_Name + ']''. – jnm2

1

Thêm một câu trả lời khác vì câu hỏi này gần như là câu hỏi thứ hai. (Không có chi tiết và chi tiết cụ thể, tôi chỉ có thể cung cấp các phác thảo chung và mã psuedo — Tôi không biết SAP.)

Hãy bắt đầu với trục xoay. Nó cần phải tạo ra các cột có nhãn, có lẽ, tháng, mà bạn đã có trong ví dụ của bạn như Table1.Col_Name, một varchar (10); các giá trị đó được trích xuất và được thêm động vào truy vấn pivot dưới dạng tên cột. Nếu không có cột nào trong cơ sở dữ liệu, thì bạn phải xây dựng nó cho truy vấn dựa trên dữ liệu người dùng nhập vào. Tôi sẽ làm việc với các giả định sau: - Dữ liệu có cột thời gian chờ, có thể tìm thấy bất kỳ giá trị nào (năm qua mili giây) - Người dùng chỉ định "ngày bắt đầu" (luôn là ngày đầu tiên của tháng?) và bạn phải tạo cột cho điều đó và 11 tháng sau, tổng hợp dữ liệu nằm trong mỗi tháng mục tiêu.

Bước 1, tôi muốn thiết lập và cư một bảng tạm thời chứa các cột 12 mục tiêu:

CREATE TABLE #Months 
(
    Col_Name varchar(10) 
    ,MonthStart datetime 
    ,MonthEnd datetime 
) 

Label được định dạng như bạn muốn nó xuất hiện, MonthStart sẽ là sự khởi đầu tuyệt đối của tháng (giả sử, ngày 1 tháng 10 năm 2011 00: 00: 00.000) và MonthEnd sẽ là khởi đầu tuyệt đối của tháng tiếp theo (ngày 1 tháng 11 năm 2011 00: 00: 00.000) - điều này cho phép bạn sử dụng SELECT … from <table> where DataDate >= MontStart and DataDate < MonthEnd để nhận tất cả dữ liệu trong tháng đó.

Tiếp theo, tham gia bảng này trên bảng dữ liệu của bạn và tổng hợp, một cái gì đó như:

SELECT 
    mt.Col_Name 
    ,sum(dt.RawData) Amount 
from #Months mt 
    inner join MyData dt 
    on dt.DataDate >= mt.MonthStart 
    and dt.DataDate < mt.MonthEnd -- Yes, ON clauses don't have to be simple equivalencies! 
    inner join <other tables as necessary for Account, AccountName, etc.> 

cắm này trong khi truy vấn trong cùng của tuyên bố trục, chiết xuất/xây dựng danh sách các Col_Names từ bảng tạm thời sử dụng truy vấn không phải XML (tôi không biết cái gì khác để gọi nó), xây dựng và thực thi động, và bạn nên tốt.

+0

Cảm ơn Philip Leon Lai –

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