簡単なクエリの4か所で同じステートメントを使用している> 100の選択肢を持つcaseステートメントを記述しました。
同じクエリを2回結合して2回実行しますが、カウントも実行するため、group byにはcaseステートメントも含まれます。
これは、同じ会社の異なるレコードの綴りが異なるいくつかの会社名のラベルを変更するためです。
変数を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''
...
Selectステートメントで使用すると、クエリはcaseステートメントをテキストとして返し、評価しませんでした。
私はそれをグループで使用することもできませんでした-このエラーメッセージが表示されました:
Each GROUP BY expression must contain at least one column that is not an outer reference.
理想的には、CASEを1か所にまとめたいと考えています。そのため、1つの行を更新して他の場所に複製しない可能性はありません。
これを行う方法はありますか?
私は他の方法を受け入れます(多分関数のようですが、このように使用する方法はわかりません)。
これは私が現在使用しているSELECTのサンプルです
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
このUNIONの目的は、ある期間のすべてのデータを返すことと、以前の12か月の同じ期間のデータを返すことです。
編集:不足している「CATCH-ALL」を追加しました
EDIT2:UNIONステートメントの2番目のAddedを追加しました
EDIT3:GROUP BYを修正して他の必要な要素を含めました
CASE式の繰り返しを排除する簡単な方法の1つは、次のようにCROSS APPLYを使用することです。
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
CROSS APPLYを使用して、ステートメント内のどこからでも参照できるように、CASE式に名前を割り当てます。厳密に言えば、計算列をnested SELECT – CROSS APPLYに続くFROMなしのSELECTで定義しているためです。
これは、派生テーブルのエイリアスされた列を参照することと同じです–技術的には、この入れ子になったSELECTと同じです。これは、相関サブクエリと派生テーブルの両方です。相関サブクエリとして、外部スコープの列を参照でき、派生テーブルとして、外部スコープが定義する列を参照できます。
同じCASE式を使用するUNIONクエリの場合、各レッグでそれを定義する必要があります。CASEの代わりに完全に異なる置換方法を使用することを除いて、その回避策はありません。ただし、特定のケースでは、UNIONなしで結果をフェッチすることが可能です。
2つのレッグは、WHERE条件のみが異なります。 1つはこれを持っています:
WHERE a.datecreated = CONVERT(DATE,now())
そして他のこれ:
WHERE a.datecreated = DATEADD(YEAR,-1,CONVERT(DATE,now()))
次のように組み合わせることができます。
WHERE a.datecreated IN (
CONVERT(DATE,now()),
DATEADD(YEAR,-1,CONVERT(DATE,now()))
)
この回答の冒頭にある変更されたSELECTに適用します。
データをテーブルに入れる
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');
それに参加します。
SELECT ...,COALESCE(AccountTranslate.translated, ac.accountName) AS accountName
FROM
....,
account_code ac left outer join
AccountTranslate at on ac.accountName LIKE AccountTranslate.wrong
これにより、複数の場所でデータを最新の状態に保つことを回避できます。必要な場所でCOALESCE
を使用するだけです。他の提案に従って、これをCTEまたはVIEW
sに組み込むことができます。
別のオプションをいくつか再利用する必要がある場合は、インラインテーブル値関数を使用するとよいでしょう。
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
あなたの選択はこのようになります。
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)
また、これはテストしていないため、コードのパフォーマンスも確認する必要があります。
EDIT1:andriyは、コードを編集するクロス適用を使用するものをすでに提供していると思います。まあ、コードの他の部分で同じことを繰り返しているので、関数の変更はすべて反映されるため、これは集中化できます。
VIEW
を使用して、あなたがやろうとしていることを行います。もちろん、基礎となるデータを修正することもできますが、このサイトでは頻繁に、質問をする人(コンサルタント/ dbas /)にはこれを行う権限がありません。 VIEW
を使用すると、この問題を解決できます!私はUPPER
関数も使用しました-このような場合のエラーを解決する安価な方法。
これで、VIEW
を宣言するのは1回だけで、どこでも使用できます!このように、データ変換アルゴリズムが保存および実行される場所が1つしかないため、システムの信頼性と堅牢性が向上します。
CTEを使用することもできます( 共通テーブル式 )-回答の下部を参照してください!
あなたの質問に答えるために、私は次のことをしました:
サンプルテーブルを作成します。
CREATE TABLE my_error (wrong VARCHAR(50));
いくつかのサンプルレコードを挿入します。
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');
次に、推奨されるようにVIEW
を作成します。
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;
次に、SELECT
からのVIEW
:
SELECT * FROM my_error_view
ORDER BY wrong;
結果:
ADDICTION ADVICE
AIR BP
AIR NEW ZEALAND
エボラ!
これらすべてはフィドル here で見つけることができます。
CTE
アプローチ:
上記と同じですが、CTE
がVIEW
の代わりに次のように使用されます。
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;
結果は同じです。その後、CTE
を他のテーブルと同様に扱うことができます-SELECT
sの場合のみ! Fiddle利用可能 ここ 。
全体として、この場合はVIEW
アプローチの方が良いと思います。
埋め込みテーブル
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
ユニオンをスキップし、他の人が提案する場所でOR
を使用します。