免責事項:データベースを使用する作業時間のごく一部としてのみ、私と一緒に我慢してください。(ほとんどの場合、仕事でC ++プログラミングを行いますが、奇数月ごとにOracleデータベースで何かを検索/修正/追加する必要があります。)
アドホッククエリとアプリケーションに組み込まれたクエリの両方で、クエリの大部分が「コード」を繰り返しただけの複雑なSQLクエリを繰り返し書く必要がありました。
伝統的なプログラミング言語で、そのような憎むべきを書く深いトラブルにあなたを得るでしょう、まだI(Iは)まだSQLクエリコードの繰り返しを防ぐために、まともな手法を見つけることができませんでした。
編集:最初に、元の例に優れた改善を提供してくれた回答者に感謝します。ただし、この質問は私の例に関するものではありません。SQLクエリの反復性についてです。そのため、これまでの回答(JackP、Leigh)は、より良いクエリを作成することで反復性を減らすことができることを示す素晴らしい仕事をしています。ただし、その場合でも、明らかに削除できない反復性に直面します。これは、常にSQLに悩まされます。「従来の」プログラミング言語では、コードの反復性を最小限に抑えるためにかなり多くのリファクタリングを行うことができますが、SQLでは、これを可能にする(?)ツールはないようです。
Oracleタグを再度削除したことに注意してください。これは、データベースやスクリプト言語が存在しないかどうかに興味があるためです。
今日私が一緒に石畳を作ったそのような宝石の一つです。基本的に、単一のテーブルの列セットの違いを報告します。次のコードをざっと読んでください。最後に大きなクエリ。以下に続きます。
--
-- Create Table to test queries
--
CREATE TABLE TEST_ATTRIBS (
id NUMBER PRIMARY KEY,
name VARCHAR2(300) UNIQUE,
attr1 VARCHAR2(2000),
attr2 VARCHAR2(2000),
attr3 INTEGER,
attr4 NUMBER,
attr5 VARCHAR2(2000)
);
--
-- insert some test data
--
insert into TEST_ATTRIBS values ( 1, 'Alfred', 'a', 'Foobar', 33, 44, 'e');
insert into TEST_ATTRIBS values ( 2, 'Batman', 'b', 'Foobar', 66, 44, 'e');
insert into TEST_ATTRIBS values ( 3, 'Chris', 'c', 'Foobar', 99, 44, 'e');
insert into TEST_ATTRIBS values ( 4, 'Dorothee', 'd', 'Foobar', 33, 44, 'e');
insert into TEST_ATTRIBS values ( 5, 'Emilia', 'e', 'Barfoo', 66, 44, 'e');
insert into TEST_ATTRIBS values ( 6, 'Francis', 'f', 'Barfoo', 99, 44, 'e');
insert into TEST_ATTRIBS values ( 7, 'Gustav', 'g', 'Foobar', 33, 44, 'e');
insert into TEST_ATTRIBS values ( 8, 'Homer', 'h', 'Foobar', 66, 44, 'e');
insert into TEST_ATTRIBS values ( 9, 'Ingrid', 'i', 'Foobar', 99, 44, 'e');
insert into TEST_ATTRIBS values (10, 'Jason', 'j', 'Bob', 33, 44, 'e');
insert into TEST_ATTRIBS values (12, 'Konrad', 'k', 'Bob', 66, 44, 'e');
insert into TEST_ATTRIBS values (13, 'Lucas', 'l', 'Foobar', 99, 44, 'e');
insert into TEST_ATTRIBS values (14, 'DUP_Alfred', 'a', 'FOOBAR', 33, 44, 'e');
insert into TEST_ATTRIBS values (15, 'DUP_Chris', 'c', 'Foobar', 66, 44, 'e');
insert into TEST_ATTRIBS values (16, 'DUP_Dorothee', 'd', 'Foobar', 99, 44, 'e');
insert into TEST_ATTRIBS values (17, 'DUP_Gustav', 'X', 'Foobar', 33, 44, 'e');
insert into TEST_ATTRIBS values (18, 'DUP_Homer', 'h', 'Foobar', 66, 44, 'e');
insert into TEST_ATTRIBS values (19, 'DUP_Ingrid', 'Y', 'foo', 99, 44, 'e');
insert into TEST_ATTRIBS values (20, 'Martha', 'm', 'Bob', 33, 88, 'f');
-- Create comparison view
CREATE OR REPLACE VIEW TA_SELFCMP as
select
t1.id as id_1, t2.id as id_2, t1.name as name, t2.name as name_dup,
t1.attr1 as attr1_1, t1.attr2 as attr2_1, t1.attr3 as attr3_1, t1.attr4 as attr4_1, t1.attr5 as attr5_1,
t2.attr1 as attr1_2, t2.attr2 as attr2_2, t2.attr3 as attr3_2, t2.attr4 as attr4_2, t2.attr5 as attr5_2
from TEST_ATTRIBS t1, TEST_ATTRIBS t2
where t1.id <> t2.id
and t1.name <> t2.name
and t1.name = REPLACE(t2.name, 'DUP_', '')
;
-- NOTE THIS PIECE OF HORRIBLE CODE REPETITION --
-- Create comparison report
-- compare 1st attribute
select 'attr1' as Different,
id_1, id_2, name, name_dup,
CAST(attr1_1 AS VARCHAR2(2000)) as Val1, CAST(attr1_2 AS VARCHAR2(2000)) as Val2
from TA_SELFCMP
where attr1_1 <> attr1_2
or (attr1_1 is null and attr1_2 is not null)
or (attr1_1 is not null and attr1_2 is null)
union
-- compare 2nd attribute
select 'attr2' as Different,
id_1, id_2, name, name_dup,
CAST(attr2_1 AS VARCHAR2(2000)) as Val1, CAST(attr2_2 AS VARCHAR2(2000)) as Val2
from TA_SELFCMP
where attr2_1 <> attr2_2
or (attr2_1 is null and attr2_2 is not null)
or (attr2_1 is not null and attr2_2 is null)
union
-- compare 3rd attribute
select 'attr3' as Different,
id_1, id_2, name, name_dup,
CAST(attr3_1 AS VARCHAR2(2000)) as Val1, CAST(attr3_2 AS VARCHAR2(2000)) as Val2
from TA_SELFCMP
where attr3_1 <> attr3_2
or (attr3_1 is null and attr3_2 is not null)
or (attr3_1 is not null and attr3_2 is null)
union
-- compare 4th attribute
select 'attr4' as Different,
id_1, id_2, name, name_dup,
CAST(attr4_1 AS VARCHAR2(2000)) as Val1, CAST(attr4_2 AS VARCHAR2(2000)) as Val2
from TA_SELFCMP
where attr4_1 <> attr4_2
or (attr4_1 is null and attr4_2 is not null)
or (attr4_1 is not null and attr4_2 is null)
union
-- compare 5th attribute
select 'attr5' as Different,
id_1, id_2, name, name_dup,
CAST(attr5_1 AS VARCHAR2(2000)) as Val1, CAST(attr5_2 AS VARCHAR2(2000)) as Val2
from TA_SELFCMP
where attr5_1 <> attr5_2
or (attr5_1 is null and attr5_2 is not null)
or (attr5_1 is not null and attr5_2 is null)
;
ご覧のとおり、「差分レポート」を生成するクエリは、同じSQL SELECTブロックを5回使用します(簡単に42回になる可能性があります!)。これは絶対に頭がおかしい(私はこれを言うことは許されますが、結局コードを書いたのです)ように感じますが、これに対する良い解決策を見つけることができませんでした。
これが実際のアプリケーションコードのクエリである場合、このクエリを文字列としてまとめて、クエリを文字列として実行する関数を作成できます。
- ->文字列を構築するのは恐ろしく、テストと保守が大変です。「アプリケーションコード」がPL / SQLなどの言語で記述されている場合、非常に間違っていると感じて痛い。
あるいは、PL / SQLなどから使用する場合、このクエリをよりメンテナンスしやすくするための手順的な手段があると思います。
- ->コードの繰り返しを防ぐために、単一のクエリで表現できるものを手順ステップに展開することも間違っていると感じます。
このクエリがデータベースのビューとして必要な場合、私が理解している限り、上記で投稿したようにビュー定義を実際に維持する以外に方法はありません。(!!?)
- ->実際、2ページビューの定義をメンテナンスする必要がありました。明らかに、このビューで何かを変更するには、同じサブステートメントが別の行で使用されていたかどうか、およびそこで変更する必要があるかどうかについて、ビュー定義に対する正規表現テキスト検索が必要でした。
それで、タイトルが行くように- そのような憎悪を書かなければならないことを防ぐためにどんなテクニックがありますか?
UNION ALL
。多くの場合UNION
、ALL
通常は、必要な並べ替え操作用の一時ストレージへのスプールになります(「UNION」のUNION ALL
後にDISTINCT
は並べ替えが暗黙的に続くため)、場合によってはパフォーマンスの違いが大きくなることがあります。