Cara memiliki lebih dari 100 entri dalam pernyataan kasus sebagai variabel

11

Saya menulis pernyataan kasus dengan> 100 pilihan di mana saya menggunakan pernyataan yang sama di 4 tempat dalam permintaan sederhana.

Kueri yang sama dua kali dengan penyatuan di antara mereka tetapi juga melakukan penghitungan dan karenanya grup juga berisi pernyataan kasus.

Ini untuk memberi label ulang beberapa nama perusahaan di mana catatan yang berbeda untuk perusahaan yang sama dieja secara berbeda.

Saya mencoba mendeklarasikan variabel sebagai 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''
       ...

Ketika saya pergi untuk menggunakannya dalam pernyataan pilih saya - permintaan hanya mengembalikan pernyataan kasus sebagai teks dan tidak mengevaluasinya.

Saya juga tidak dapat menggunakannya dalam grup oleh - Saya mendapat pesan kesalahan ini:

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

Idealnya saya ingin memiliki KASUS hanya di satu tempat - sehingga tidak ada kesempatan saya memperbarui satu baris dan tidak mereplikasi itu di tempat lain.

Apakah ada cara untuk melakukan ini?

Saya terbuka untuk cara lain (Seperti mungkin suatu fungsi - tapi saya tidak yakin bagaimana menggunakannya seperti ini)

Berikut adalah contoh SELECT yang saya gunakan saat ini

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

Tujuan untuk UNION ini adalah untuk mengembalikan semua data untuk periode waktu, dan JUGA untuk mengembalikan data untuk periode yang sama selama 12 bulan sebelumnya

EDIT: Menambahkan hilang "CATCH-ALL"
EDIT2: Menambahkan ½ kedua dari pernyataan UNION
EDIT3: Koreksi GROUP BY untuk memasukkan beberapa elemen yang diperlukan lainnya

kiltannen
sumber
Apa perbedaan 2 bagian dari UNION? Mereka terlihat sangat mirip, kecuali untuk kondisi WHERE yang sedikit berbeda.
ypercubeᵀᴹ
Itulah perbedaan utama. Dua kondisi WHERE berbeda pada tanggal berikan hari ini & tanggal yang sama 12 bulan lalu. Ini berarti saya kemudian dapat membandingkan angka untuk hari itu dan dan hari yang sama 12 bulan yang lalu di lapisan presentasi - tetapi menjalankan query SQL tunggal.
kiltannen
3
Mengapa tidak memilih SELECT tunggal WHERE a.datecreated = CONVERT(DATE,now()) OR a.datecreated = DATEADD(YEAR,-1,CONVERT(DATE,now()))?
ypercubeᵀᴹ
@ ypercubeᵀᴹ Jawaban sederhananya adalah ketika membangun ini pada awalnya saya menyalin cara saya melakukannya di tempat lain yang menggunakan UNION. Yang sedikit lebih rumit adalah bahwa pembatas tanggal sebenarnya cukup kompleks daripada hari ini dan tanggal yang sama 12 bulan yang lalu. Kisaran tanggal yang saya pilih adalah dari 1 Juli hingga tanggal saat ini + dari 1 Juli sebelum itu hingga tanggal 12 bulan yang lalu. (Tahun Keuangan Hingga Tanggal VS TA Terakhir YTD 12 bulan lalu - ini memberikan perbandingan pertumbuhan atau sebaliknya untuk tahun keuangan). TAPI seperti yang AndryM & Anda sarankan, saya akan mencoba minus UNION
kiltannen

Jawaban:

11

Salah satu cara mudah untuk menghilangkan pengulangan ekspresi CASE adalah dengan menggunakan CROSS APPLY seperti ini:

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

Dengan bantuan CROSS BERLAKU Anda menetapkan nama untuk ekspresi KASUS Anda sedemikian rupa sehingga dapat dirujuk di mana saja dalam pernyataan Anda. Ini berfungsi karena secara tegas Anda mendefinisikan kolom yang dikomputasi dalam SELECT bersarang - SELECT DARI-kurang yang mengikuti CROSS BERLAKU.

Ini sama dengan mereferensikan kolom alias dari tabel turunan - yang secara teknis SELECT bersarang ini. Ini adalah subquery yang berkorelasi dan tabel turunan. Sebagai subquery yang dikorelasikan, diizinkan untuk mereferensikan kolom lingkup luar, dan sebagai tabel turunan memungkinkan lingkup luar untuk referensi kolom yang didefinisikannya.

Untuk kueri UNION yang menggunakan ekspresi KASUS yang sama, Anda harus mendefinisikannya di setiap kaki, tidak ada solusi untuk itu kecuali menggunakan metode penggantian yang sama sekali berbeda daripada KASUS. Namun, dalam kasus spesifik Anda dimungkinkan untuk mengambil hasilnya tanpa UNION.

Kedua kaki berbeda dalam kondisi WHERE saja. Seseorang memiliki ini:

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

dan yang lainnya ini:

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

Anda dapat menggabungkan mereka seperti ini:

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

dan menerapkannya pada SELECT yang dimodifikasi pada awal jawaban ini.

Andriy M
sumber
Bagus satu Andriy - +1! Terinspirasi oleh Anda :-), saya telah menambahkan pendekatan lain untuk jawaban saya - a CTE- Saya tidak yakin mana pendekatan terbaik!
Vérace
Hai Andriy, saya suka tampilan solusi ini. Saya memang menyebutkan bahwa saya memiliki UNION - tetapi saya cukup bodoh untuk tidak memasukkannya ke dalam contoh saya. Saya sudah melakukannya sekarang. Saya curiga x dari CROSS APPLY ini sepertinya tidak akan tersedia untuk paruh kedua UNION, kan? Jadi ini berarti saya masih akan terjebak dengan 2 salinan KASUS itu benar? (Saya akan memeriksanya besok ketika saya kembali bekerja)
kiltannen
@kiltannen Jatuhkan UNIONdan cukup sertakan datecreatedkolom di GROUP BYklausa Anda (dan perbarui WHEREklausa untuk menyertakan kedua tanggal yang Anda minati).
Scott M
@ScottM: Saya tidak berpikir OP perlu memasukkan datecreatedkolom dalam GROUP BY. Selain itu, saya sepenuhnya setuju, mereka hanya bisa menggabungkan klausa WHERE dan membuang UNION.
Andriy M
@ scott-m saya harus mencoba ini besok TETAPI saya kira itu tidak bekerja dengan baik. Sebenarnya bukan satu hari - ini berpotensi beberapa bulan. Saya pikir apa yang saya temui adalah memiliki data harian hingga 11 bulan - jadi dari mana mulai & berakhir DAN kemudian saya harus menjalankan OR untuk periode yang sama 12 bulan sebelumnya. Saya pikir ini berakhir dengan hit kinerja. Saya harus mencoba lagi - tetapi saya ingat mengalami masalah yang tidak saya miliki ketika menjalankan UNION. Tentu saja itu memang membawa masalah tersendiri. Seperti yang saat ini saya
geluti
22

Masukkan data ke dalam tabel

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');

dan bergabunglah untuk itu.

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

Dengan begitu Anda dapat menghindari memperbarui data di banyak tempat. Cukup gunakan di COALESCEmana Anda membutuhkannya. Anda dapat memasukkan ini ke dalam CTE atau VIEWs sesuai saran lainnya.

LoztInSpace
sumber
4

Opsi lain yang saya pikir jika Anda perlu menggunakannya kembali di beberapa tempat, fungsi nilai tabel Inline akan bagus.

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

Pilihan Anda akan seperti ini.

  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)

Juga, saya belum menguji ini dan kinerja kode juga harus ditentukan.

EDIT1 : Saya pikir andriy sudah memberikan satu yang menggunakan cross berlaku yang redacts kode. Nah, yang ini bisa terpusat karena setiap perubahan dalam fungsi akan mencerminkan semuanya karena Anda mengulangi hal yang sama di bagian lain dari kode.

Biju jose
sumber
3

Saya akan menggunakan VIEWuntuk melakukan apa yang Anda coba lakukan. Anda tentu saja dapat memperbaiki data yang mendasarinya, tetapi seringkali di situs ini, mereka yang mengajukan pertanyaan (konsultan / dBA /) tidak memiliki wewenang untuk melakukan ini. Menggunakan VIEWkaleng memecahkan masalah ini! Saya juga memanfaatkan UPPERfungsi - cara murah untuk menyelesaikan kesalahan dalam kasus seperti ini.

Sekarang, Anda hanya mendeklarasikan VIEWsekali dan dapat menggunakannya di mana saja! Dengan cara ini, Anda hanya memiliki satu tempat di mana algoritma konversi data Anda disimpan dan dijalankan, sehingga meningkatkan keandalan dan ketahanan sistem Anda.

Anda juga dapat menggunakan CTE ( Common Table Expression ) - lihat bagian bawah jawabannya!

Untuk menjawab pertanyaan Anda, saya melakukan yang berikut:

Buat tabel sampel:

CREATE TABLE my_error (wrong VARCHAR(50));

Masukkan beberapa catatan sampel:

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');

Kemudian buat VIEWseperti yang disarankan:

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;

Lalu, SELECT dari Anda VIEW:

SELECT * FROM my_error_view
ORDER BY wrong;

Hasil:

ADDICTION ADVICE
AIR BP
AIR NEW ZEALAND

Dan lagi!

Anda dapat menemukan semua ini di biola di sini .

The CTEPendekatan:

Sama seperti di atas, kecuali untuk yang CTEdiganti VIEWsebagai berikut:

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;

Hasilnya sama. Anda kemudian dapat memperlakukan CTEseperti halnya meja lainnya - SELECThanya untuk! Biola tersedia di sini .

Secara keseluruhan, saya pikir VIEWpendekatannya lebih baik dalam hal ini!

Vérace
sumber
0

Meja tertanam

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 

Lewati serikat dan gunakan ORdi tempat seperti yang disarankan oleh orang lain.

paparazzo
sumber