Làm thế nào để có hơn 100 mục trong câu lệnh tình huống dưới dạng một biến


11

Tôi đã viết một câu lệnh tình huống với> 100 lựa chọn trong đó tôi đang sử dụng cùng một câu lệnh ở 4 vị trí trong một truy vấn đơn giản.

Truy vấn tương tự hai lần với một liên kết giữa chúng nhưng cũng đang thực hiện đếm và do đó nhóm cũng chứa câu lệnh tình huống.

Điều này là để dán lại một số tên công ty trong đó các hồ sơ khác nhau cho cùng một công ty được đánh vần khác nhau.

Tôi đã cố gắng khai báo một biến là VarChar (MAX)

declare @CaseForAccountConsolidation varchar(max)

SET @CaseForAccountConsolidation = 'CASE 
       WHEN ac.accountName like ''AIR NEW Z%'' THEN ''AIR NEW ZEALAND''
       WHEN ac.accountName LIKE ''AIR BP%'' THEN ''AIR BP''
       WHEN ac.accountName LIKE ''ADDICTION ADVICE%'' THEN ''ADDICTION ADVICE''
       WHEN ac.accountName LIKE ''AIA%'' THEN ''AIA''
       ...

Khi tôi đi sử dụng nó trong câu lệnh chọn của mình - truy vấn chỉ trả về câu lệnh tình huống dưới dạng văn bản và không đánh giá nó.

Tôi cũng không thể sử dụng nó trong nhóm bởi - Tôi nhận được thông báo lỗi này:

Each GROUP BY expression must contain at least one column that is not an outer reference.

Lý tưởng nhất là tôi muốn có CASE ở một nơi duy nhất - để tôi không có cơ hội cập nhật một dòng và không sao chép nó ở nơi khác.

Có một số cách để làm điều này?

Tôi mở cho các cách khác (Có thể là một hàm - nhưng tôi không chắc cách sử dụng chúng như thế này)

Đây là một mẫu của CHỌN tôi hiện đang sử dụng

SELECT 
   SUM(c.charge_amount) AS GSTExcl
   ,dl.FirstDateOfMonth AS MonthBilled
   ,dl.FirstDateOfWeek AS WeekBilled
   ,CASE 
       WHEN ac.accountName like 'AIR NEW Z%' THEN 'AIR NEW ZEALAND'
       WHEN ac.accountName LIKE 'AIR BP%' THEN 'AIR BP'
       WHEN ac.accountName LIKE 'ADDICTION ADVICE%' THEN 'ADDICTION ADVICE'
       WHEN ac.accountName LIKE 'AIA%' THEN 'AIA'
       ELSE ac.accountName
   END AS accountName
   ,dl.FinancialYear
   ,CONVERT(Date,c.date_charged) AS date_charged
FROM [accession] a
   LEFT JOIN account_code ac ON a.account_code_id = ac.account_code_id
   LEFT Join charge c ON a.accession_id = c.accession_id
   LEFT JOIN dateLookup dl ON convert(date,c.date_charged) = dl.date
WHERE a.datecreated = CONVERT(DATE,now())
GROUP BY
   dl.FirstDateOfMonth
   ,dl.FinancialYear
   ,dl.FirstDateOfWeek
   ,CONVERT(Date,c.date_charged)
   ,CASE 
       WHEN ac.accountName like 'AIR NEW Z%' THEN 'AIR NEW ZEALAND'
       WHEN ac.accountName LIKE 'AIR BP%' THEN 'AIR BP'
       WHEN ac.accountName LIKE 'ADDICTION ADVICE%' THEN 'ADDICTION ADVICE'
       WHEN ac.accountName LIKE 'AIA%' THEN 'AIA'
       ELSE ac.accountName
   END

UNION

SELECT 
   SUM(c.charge_amount) AS GSTExcl
   ,dl.FirstDateOfMonth AS MonthBilled
   ,dl.FirstDateOfWeek AS WeekBilled
   ,CASE 
       WHEN ac.accountName like 'AIR NEW Z%' THEN 'AIR NEW ZEALAND'
       WHEN ac.accountName LIKE 'AIR BP%' THEN 'AIR BP'
       WHEN ac.accountName LIKE 'ADDICTION ADVICE%' THEN 'ADDICTION ADVICE'
       WHEN ac.accountName LIKE 'AIA%' THEN 'AIA'
       ELSE ac.accountName
   END AS accountName
   ,dl.FinancialYear
   ,CONVERT(Date,c.date_charged) AS date_charged
FROM [accession] a
   LEFT JOIN account_code ac ON a.account_code_id = ac.account_code_id
   LEFT Join charge c ON a.accession_id = c.accession_id
   LEFT JOIN dateLookup dl ON convert(date,c.date_charged) = dl.date
WHERE a.datecreated = DATEADD(YEAR,-1,CONVERT(DATE,now()))
GROUP BY
   dl.FirstDateOfMonth
   ,dl.FinancialYear
   ,dl.FirstDateOfWeek
   ,CONVERT(Date,c.date_charged)
   ,CASE 
       WHEN ac.accountName like 'AIR NEW Z%' THEN 'AIR NEW ZEALAND'
       WHEN ac.accountName LIKE 'AIR BP%' THEN 'AIR BP'
       WHEN ac.accountName LIKE 'ADDICTION ADVICE%' THEN 'ADDICTION ADVICE'
       WHEN ac.accountName LIKE 'AIA%' THEN 'AIA'
       ELSE ac.accountName
   END

Mục đích của UNION này là trả về tất cả dữ liệu cho đồng hồ bấm giờ và CSONG để trả lại dữ liệu cho cùng một đồng hồ trong 12 tháng trước

EDIT: Đã thêm
EDIT2 "CATCH-ALL" bị thiếu : Đã thêm một giây ½ của câu lệnh UNION
EDIT3: Đã sửa NHÓM THEO để bao gồm một số yếu tố cần thiết khác


Làm thế nào để 2 phần của UNION khác nhau? Chúng trông khá giống nhau, ngoại trừ các điều kiện WHERE hơi khác nhau.
ypercubeᵀᴹ

Đó là sự khác biệt chính. Hai điều kiện WHERE khác nhau vào ngày đưa ra hôm nay & cùng ngày 12 tháng trước. Điều này có nghĩa là sau đó tôi có thể so sánh các số cho ngày hôm đó và cùng ngày 12 tháng trước trong lớp trình bày - nhưng chạy truy vấn SQL duy nhất.
kiltannen

3
Tại sao không phải là một CHỌN với WHERE a.datecreated = CONVERT(DATE,now()) OR a.datecreated = DATEADD(YEAR,-1,CONVERT(DATE,now()))?
ypercubeᵀᴹ

@ ypercubeᵀᴹ Câu trả lời đơn giản là khi xây dựng nó lúc đầu tôi đã sao chép một cách mà tôi đã làm ở một nơi khác sử dụng UNION. Điều phức tạp hơn một chút là bộ giới hạn ngày thực sự khá phức tạp so với ngày nay và cùng ngày 12 tháng trước. Phạm vi ngày tôi đang chọn là từ ngày 1 tháng 7 đến ngày hiện tại + từ ngày 1 tháng 7 trước đó đến ngày chính xác là 12 tháng trước. (Năm tài chính đến ngày VS năm ngoái 12 năm trước - điều này đưa ra sự so sánh về tăng trưởng hoặc mặt khác cho năm tài chính). NHƯNG như AndryM và bạn đề nghị, tôi sẽ thử trừ UNION
kiltannen

Câu trả lời:


11

Một cách dễ dàng để loại bỏ sự lặp lại của biểu thức CASE là sử dụng CROSS ỨNG DỤNG như thế này:

SELECT 
   SUM(c.charge_amount) AS GSTExcl
   ,dl.FirstDateOfMonth AS MonthBilled
   ,dl.FirstDateOfWeek AS WeekBilled
   ,x.accountName
   ,dl.FinancialYear
   ,CONVERT(Date,c.date_charged) AS date_charged
FROM [accession] a
   LEFT JOIN account_code ac ON a.account_code_id = ac.account_code_id
   CROSS APPLY
   (
    SELECT 
       CASE 
           WHEN ac.accountName like 'AIR NEW Z%' THEN 'AIR NEW ZEALAND'
           WHEN ac.accountName LIKE 'AIR BP%' THEN 'AIR BP'
           WHEN ac.accountName LIKE 'ADDICTION ADVICE%' THEN 'ADDICTION ADVICE'
           WHEN ac.accountName LIKE 'AIA%' THEN 'AIA'
       END AS accountName
   ) AS x
   LEFT Join charge c ON a.accession_id = c.accession_id
   LEFT JOIN dateLookup dl ON convert(date,c.date_charged) = dl.date
GROUP BY
   dl.FirstDateOfMonth
   ,x.AccountName

Với sự trợ giúp của CROSS ỨNG DỤNG, bạn chỉ định một tên cho biểu thức CASE của bạn theo cách mà nó có thể được tham chiếu ở bất cứ đâu trong câu lệnh của bạn. Nó hoạt động bởi vì nói đúng ra bạn đang xác định cột được tính toán trong một CHỌN lồng nhau - CHỌN TỪ ít đi theo ỨNG DỤNG CROSS.

Điều này giống như việc tham chiếu một cột bí danh của bảng dẫn xuất - về mặt kỹ thuật, cái CHỌN lồng nhau này là về mặt kỹ thuật. Nó vừa là truy vấn con tương quan vừa là bảng dẫn xuất. Là một truy vấn con tương quan, nó được phép tham chiếu các cột của phạm vi bên ngoài và như một bảng dẫn xuất, nó cho phép phạm vi bên ngoài tham chiếu các cột mà nó xác định.

Đối với truy vấn UNION sử dụng cùng một biểu thức CASE, bạn phải xác định nó trong mỗi chân, không có cách giải quyết nào cho điều đó ngoại trừ sử dụng một phương pháp thay thế hoàn toàn khác thay vì CASE. Tuy nhiên, trong trường hợp cụ thể của bạn, có thể lấy kết quả mà không cần UNION.

Hai chân chỉ khác nhau trong điều kiện WHERE. Người ta có cái này:

WHERE a.datecreated = CONVERT(DATE,now())

và cái khác này:

WHERE a.datecreated = DATEADD(YEAR,-1,CONVERT(DATE,now()))

Bạn có thể kết hợp chúng như thế này:

WHERE a.datecreated IN (
                        CONVERT(DATE,now()),
                        DATEADD(YEAR,-1,CONVERT(DATE,now()))
                       )

và áp dụng nó cho CHỌN đã sửa đổi ở đầu câu trả lời này.


Một người đẹp - +1! Lấy cảm hứng từ bạn :-), tôi đã thêm một cách tiếp cận khác vào câu trả lời của mình - a CTE- Tôi không chắc đó là cách tiếp cận tốt nhất!
Vérace

Xin chào Andriy, tôi thích giao diện của giải pháp này. Tôi đã đề cập rằng tôi đã có một ĐOÀN KẾT - nhưng tôi đủ ngu ngốc để không đưa nó vào ví dụ của mình. Tôi đã làm như vậy bây giờ. Tôi nghi ngờ x này từ CROSS ỨNG DỤNG không có khả năng có sẵn cho nửa sau của UNION phải không? Vì vậy, điều này có nghĩa là tôi vẫn sẽ bị mắc kẹt với 2 bản sao của CASE phải không? (Tôi sẽ kiểm tra nó vào ngày mai khi tôi trở lại làm việc)
kiltannen

@kiltannen Bỏ UNIONvà chỉ bao gồm datecreatedcột trong GROUP BYmệnh đề của bạn (và cập nhật WHEREmệnh đề để bao gồm cả hai ngày mà bạn quan tâm).
Scott M

@ScottM: Tôi không nghĩ OP cần đưa datecreatedcột vào NHÓM THEO. Ngoài ra, tôi hoàn toàn đồng ý, họ chỉ có thể kết hợp các mệnh đề WHERE và bỏ qua ĐOÀN.
Andriy M

@ scott-m Tôi sẽ phải thử vào ngày mai NHƯNG tôi nghi ngờ rằng nó không hoạt động tốt như vậy. Nó thực sự không phải là một ngày - nó có khả năng vài tháng. Tôi nghĩ rằng những gì tôi gặp phải là có tới 11 tháng dữ liệu hàng ngày - vì vậy nơi bắt đầu và kết thúc VÀ sau đó tôi phải chạy OR trong cùng khoảng thời gian 12 tháng trước đó. Tôi nghĩ rằng điều này đã kết thúc với một hit hiệu suất. Tôi phải thử lại - nhưng tôi nhớ đã gặp phải những vấn đề mà tôi không gặp phải khi chạy UNION. Tất nhiên điều đó mang lại vấn đề của riêng nó. Giống như người mà tôi hiện đang vật lộn với ..
kiltannen

22

Đặt dữ liệu vào một bảng

CREATE TABLE AccountTranslate (wrong VARCHAR(50), translated(VARCHAR(50));

INSERT INTO AccountTranslate VALUES ('ADDICTION ADVICE%','ADDICTION ADVICE');
INSERT INTO AccountTranslate VALUES ('AIR BP%','AIR BP');
INSERT INTO AccountTranslate VALUES ('AIR NEW Z%', 'AIR NEW ZEALAND');

và tham gia với nó.

SELECT ...,COALESCE(AccountTranslate.translated, ac.accountName) AS accountName
FROM
...., 
account_code ac left outer join 
AccountTranslate at on ac.accountName LIKE AccountTranslate.wrong

Bằng cách đó bạn có thể tránh việc cập nhật dữ liệu ở nhiều nơi. Chỉ cần sử dụng những COALESCEnơi bạn cần nó. Bạn có thể kết hợp điều này vào CTE hoặc VIEWs theo các đề xuất khác.


4

Một tùy chọn khác tôi nghĩ rằng nếu bạn cần sử dụng lại nó, một vài nơi chức năng có giá trị của bảng Inline sẽ là một lựa chọn tốt.

CREATE FUNCTION dbo.itvf_CaseForAccountConsolidation
    ( @au_lname VARCHAR(8000) ) 
RETURNS TABLE 
RETURN 
SELECT  
  CASE
    WHEN UPPER(@au_lname) LIKE 'ADDICTION ADVICE%' THEN 'ADDICTION ADVICE'
    WHEN UPPER(@au_lname) LIKE 'AIR BP%'  THEN 'AIR BP'
    WHEN UPPER(@au_lname) LIKE 'AIR NEW Z%' THEN 'AIR NEW ZEALAND'
    ELSE '****ERROR****'  -- you may or may not need this! 
                         -- If converting every record, then yes, if not, then no!
                         -- Errors should stand out on browsing and it's easy to search for!
  END AS wrong

--Copied from verace

Lựa chọn của bạn sẽ như thế này.

  SELECT 
   SUM(c.charge_amount) AS GSTExcl
   ,dl.FirstDateOfMonth AS MonthBilled
   ,dl.FirstDateOfWeek AS WeekBilled
   ,dd.wrong AS accountName
   ,dl.FinancialYear
   ,CONVERT(Date,c.date_charged) AS date_charged
FROM [accession] a
   LEFT JOIN account_code ac ON a.account_code_id = ac.account_code_id
   LEFT Join charge c ON a.accession_id = c.accession_id
   LEFT JOIN dateLookup dl ON convert(date,c.date_charged) = dl.date
   CROSS APPLY  dbo.itvf_CaseForAccountConsolidation( ac.accountName)dd
GROUP BY
   dl.FirstDateOfMonth 
   ,dl.FirstDateOfWeek 
   ,wrong 
   ,dl.FinancialYear
   ,CONVERT(Date,c.date_charged)

Ngoài ra, tôi chưa kiểm tra điều này và hiệu suất của mã cũng nên được xác định.

EDIT1 : Tôi nghĩ andriy đã đưa ra một ứng dụng sử dụng chéo để tái mã. Chà, cái này có thể được tập trung vì mọi thay đổi trong hàm sẽ phản ánh tất cả vì bạn đang lặp lại tương tự trong các phần khác của mã.


3

Tôi sẽ sử dụng một VIEWđể làm những gì bạn đang cố gắng làm. Tất nhiên, bạn có thể sửa dữ liệu cơ bản, nhưng thường xuyên trên trang web này, những người đặt câu hỏi (chuyên gia tư vấn / dbas /) không có thẩm quyền để làm điều này. Sử dụng một VIEWcó thể giải quyết vấn đề này! Tôi cũng đã sử dụng UPPERchức năng - một cách rẻ tiền để giải quyết lỗi trong các trường hợp như thế này.

Bây giờ, bạn chỉ khai báo VIEWmột lần và có thể sử dụng nó ở bất cứ đâu! Theo cách này, bạn chỉ có một nơi mà thuật toán chuyển đổi dữ liệu của bạn được lưu trữ và chạy, do đó làm tăng độ tin cậy và mạnh mẽ của hệ thống của bạn.

Bạn cũng có thể sử dụng CTE ( Biểu thức bảng chung ) - xem phần dưới của câu trả lời!

Để trả lời câu hỏi của bạn, tôi đã làm như sau:

Tạo một bảng mẫu:

CREATE TABLE my_error (wrong VARCHAR(50));

Chèn một vài bản ghi mẫu:

INSERT INTO my_error VALUES ('Addiction Advice Services Ltd.');
INSERT INTO my_error VALUES ('AIR BP_and-mistake');
INSERT INTO my_error VALUES ('AIR New Zealand Airlines');

Sau đó tạo một VIEWnhư được đề xuất:

CREATE VIEW my_error_view AS 
SELECT 
  CASE
    WHEN UPPER(wrong) LIKE 'ADDICTION ADVICE%' THEN 'ADDICTION ADVICE'
    WHEN UPPER(wrong) LIKE 'AIR BP%'  THEN 'AIR BP'
    WHEN UPPER(wrong) LIKE 'AIR NEW Z%' THEN 'AIR NEW ZEALAND'
    ELSE '***ERROR****' -- You may or may not need this.
                        -- It's attention grabbing (report) and easy to search for (SQL)!
  END AS wrong
FROM my_error;

Sau đó, SELECT từ của bạn VIEW:

SELECT * FROM my_error_view
ORDER BY wrong;

Kết quả:

ADDICTION ADVICE
AIR BP
AIR NEW ZEALAND

Et voilà!

Bạn có thể tìm thấy tất cả những điều này trên fiddle ở đây .

Cách CTEtiếp cận:

Tương tự như trên, ngoại trừ CTEthay thế cho VIEWnhư sau:

WITH my_cte AS
(
  SELECT 
  CASE
    WHEN UPPER(wrong) LIKE 'ADDICTION ADVICE%' THEN 'ADDICTION ADVICE'
    WHEN UPPER(wrong) LIKE 'AIR BP%'  THEN 'AIR BP'
    WHEN UPPER(wrong) LIKE 'AIR NEW Z%' THEN 'AIR NEW ZEALAND'
    ELSE '****ERROR****'  -- you may or may not need this! 
                         -- If converting every record, then yes, if not, then no!
                         -- Errors should stand out on browsing and it's easy to search for!
  END AS wrong
  FROM my_error
)
SELECT * FROM my_cte;

Kết quả là như nhau. Sau đó, bạn có thể đối xử CTEnhư bạn muốn với bất kỳ bảng nào khác - chỉ dành cho SELECTs! Fiddle có sẵn ở đây .

Nhìn chung, tôi nghĩ rằng VIEWcách tiếp cận là tốt hơn trong trường hợp này!


0

Bảng nhúng

select id, tag, trans.val 
  from [consecutive] c
  join ( values ('AIR NEW Z%', 'AIR NEW ZEALAND'),
                ('AIR BP%',    'AIR BP')
       ) trans (lk, val)
    on c.description like trans.lk 

Bỏ qua công đoàn và sử dụng một ORtrong những nơi được đề xuất bởi những người khác.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.