243

リレーショナル データベースを扱っている私たち全員が、SQL が他とは異なることを知っています (または学んでいます)。望ましい結果を引き出し、それを効率的に行うには、なじみのないパラダイムを学習し、最もよく知られているプログラミング パターンのいくつかがここでは機能しないことを発見することを部分的に特徴とする、退屈なプロセスが必要です。あなたが見た (またはあなた自身がコミットした) 一般的なアンチパターンは何ですか?

4

39 に答える 39

162

ほとんどのプログラマーがデータ アクセス レイヤーに UI ロジックを混在させる傾向があることに、私は一貫してがっかりしています。

SELECT
    FirstName + ' ' + LastName as "Full Name",
    case UserRole
        when 2 then "Admin"
        when 1 then "Moderator"
        else "User"
    end as "User's Role",
    case SignedIn
        when 0 then "Logged in"
        else "Logged out"
    end as "User signed in?",
    Convert(varchar(100), LastSignOn, 101) as "Last Sign On",
    DateDiff('d', LastSignOn, getDate()) as "Days since last sign on",
    AddrLine1 + ' ' + AddrLine2 + ' ' + AddrLine3 + ' ' +
        City + ', ' + State + ' ' + Zip as "Address",
    'XXX-XX-' + Substring(
        Convert(varchar(9), SSN), 6, 4) as "Social Security #"
FROM Users

通常、プログラマーはデータセットをグリッドに直接バインドすることを意図しているため、これを行います。SQL Server のフォーマットは、クライアントでフォーマットするよりもサーバー側でフォーマットする方が便利です。

上記のようなクエリは、データ レイヤーと UI レイヤーが密接に結合されているため、非常に脆弱です。その上、このスタイルのプログラミングでは、ストアド プロシージャの再利用を徹底的に防ぎます。

于 2008-12-06T21:55:16.103 に答える
120

これが私のトップ3です。

番号 1. フィールド リストの指定の失敗。(編集: 混乱を避けるため: これは製品コードの規則です。私が作成者でない限り、1 回限りの分析スクリプトには適用されません。)

SELECT *
Insert Into blah SELECT *

する必要があります

SELECT fieldlist
Insert Into blah (fieldlist) SELECT fieldlist

番号 2. カーソルと while ループを使用します。ループ変数を使用した while ループで十分です。

DECLARE @LoopVar int

SET @LoopVar = (SELECT MIN(TheKey) FROM TheTable)
WHILE @LoopVar is not null
BEGIN
  -- Do Stuff with current value of @LoopVar
  ...
  --Ok, done, now get the next value
  SET @LoopVar = (SELECT MIN(TheKey) FROM TheTable
    WHERE @LoopVar < TheKey)
END

番号 3. 文字列型による DateLogic。

--Trim the time
Convert(Convert(theDate, varchar(10), 121), datetime)

する必要があります

--Trim the time
DateAdd(dd, DateDiff(dd, 0, theDate), 0)

最近、「1 つのクエリは 2 つよりも優れていますよね?」という急増が見られました。

SELECT *
FROM blah
WHERE (blah.Name = @name OR @name is null)
  AND (blah.Purpose = @Purpose OR @Purpose is null)

このクエリには、パラメーターの値に応じて、2 つまたは 3 つの異なる実行プランが必要です。この SQL テキストの実行計画が 1 つだけ生成され、キャッシュに入れられます。そのプランは、パラメーターの値に関係なく使用されます。これにより、パフォーマンスが断続的に低下します。2 つのクエリ (意図した実行プランごとに 1 つのクエリ) を作成することをお勧めします。

于 2008-12-06T19:54:03.570 に答える
74
  • 人間が読めるパスワード フィールド、egad。自明。

  • インデックス付きの列に対して LIKEを使用 すると、一般的に LIKE とだけ言いたくなります。

  • SQL によって生成された PK 値のリサイクル。

  • 驚いたことに、誰もまだ神のテーブルについて言及していません。100 列のビット フラグ、大きな文字列、整数ほど「有機的」とは言えません。

  • 次に、「.ini ファイルが恋しい」パターンがあります。CSV、パイプで区切られた文字列、またはその他の解析に必要なデータを大きなテキスト フィールドに保存します。

  • また、MS SQL サーバーの場合、カーソルはまったく使用されません。特定のカーソルタスクを実行するためのより良い方法があります。

多いので編集!

于 2008-12-06T20:11:47.590 に答える
64

深く掘り下げる必要はありません。準備済みステートメントを使用しないでください。

于 2008-12-06T19:45:49.280 に答える
58

無意味なテーブル エイリアスの使用:

from employee t1,
department t2,
job t3,
...

大規模な SQL ステートメントの読み取りが必要以上に困難になる

于 2008-12-06T19:59:54.120 に答える
56
var query = "select COUNT(*) from Users where UserName = '" 
            + tbUser.Text 
            + "' and Password = '" 
            + tbPassword.Text +"'";
  1. ユーザー入力を盲目的に信頼する
  2. パラメータ化されたクエリを使用しない
  3. 平文パスワード
于 2008-12-06T23:49:50.427 に答える
47

私のバグベアは、マネージングディレクターの親友である犬のグルーマーの8歳の息子によってまとめられた450列のアクセステーブルと、誰かがデータ構造を適切に正規化する方法を知らないためにのみ存在する危険なルックアップテーブルです。

通常、このルックアップテーブルは次のようになります。

ID INT、
名前NVARCHAR(132)、
IntValue1 INT、
IntValue2 INT、
CharValue1 NVARCHAR(255)、
CharValue2 NVARCHAR(255)、
Date1 DATETIME、
Date2 DATETIME

私は、このような忌まわしきものに依存するシステムを持っている私が見たクライアントの数の数を失いました。

于 2008-12-06T20:25:25.880 に答える
28

私が最も嫌いなものは

  1. テーブルや sprocs などを作成するときにスペースを使用します。キャメルケースまたは under_scores、単数または複数形、大文字または小文字は問題ありませんが、[スペースを含む] テーブルまたは列を参照する必要があります。私はこれに出くわしました)本当に私を苛立たせます。

  2. 非正規化データ。テーブルを完全に正規化する必要はありませんが、従業員の現在の評価スコアや主要なものに関する情報を含むテーブルに出くわすと、おそらく別のテーブルを作成する必要があることがわかります。次に、同期を維持してください。最初にデータを正規化し、非正規化が役立つ場所があれば検討します。

  3. ビューまたはカーソルの過剰使用。ビューには目的がありますが、各テーブルがビューにラップされていると、多すぎます。カーソルを数回使用する必要がありましたが、通常、これには他のメカニズムを使用できます。

  4. アクセス。プログラムはアンチパターンになり得ますか? 私の職場には SQL Server がありますが、その可用性、「使いやすさ」、および非技術者ユーザーへの「親しみやすさ」のために、多くの人がアクセスを使用しています。ここには入り込むことが多すぎますが、同様の環境にいたことがある場合はわかります。

于 2008-12-06T22:34:37.663 に答える
26

カスタム プロシージャではなくシステム プロシージャの場所で最初に検索されるため、ストア プロシージャ名のプレフィックスとして SP を使用します。

于 2008-12-06T19:48:32.677 に答える
25

一時テーブルとカーソルの過剰使用。

于 2008-12-06T20:08:09.847 に答える
25

時刻の値を格納するには、UTC タイムゾーンのみを使用する必要があります。現地時間は使用しないでください。

于 2009-04-20T22:14:10.167 に答える
23

意図されていないものに「デッド」フィールドを再利用する(たとえば、「ファックス」フィールドにユーザーデータを保存する)-しかし、簡単な修正として非常に魅力的です!

于 2010-02-08T17:16:34.617 に答える
23

SCOPE_IDENTITY() の代わりに@@IDENTITY を使用する

この回答から引用:

  • @@IDENTITYは、すべてのスコープにわたって、現在のセッションの任意のテーブルに対して生成された最後の ID 値を返します。範囲をまたいでいるので注意が必要です。現在のステートメントではなく、トリガーから値を取得できます。
  • SCOPE_IDENTITYは、現在のセッションおよび現在のスコープで任意のテーブルに対して生成された最後の ID 値を返します。一般的に使用したいもの。
  • IDENT_CURRENTは、任意のセッションおよび任意のスコープで特定のテーブルに対して生成された最後の ID 値を返します。これにより、上記の 2 つが必要なものではない場合 (非常にまれ) に、どのテーブルから値を取得するかを指定できます。レコードを挿入していないテーブルの現在の IDENTITY 値を取得する場合は、これを使用できます。
于 2009-04-06T15:22:51.597 に答える
21
SELECT FirstName + ' ' + LastName as "Full Name", case UserRole when 2 then "Admin" when 1 then "Moderator" else "User" end as "User's Role", case SignedIn when 0 then "Logged in" else "Logged out" end as "User signed in?", Convert(varchar(100), LastSignOn, 101) as "Last Sign On", DateDiff('d', LastSignOn, getDate()) as "Days since last sign on", AddrLine1 + ' ' + AddrLine2 + ' ' + AddrLine3 + ' ' + City + ', ' + State + ' ' + Zip as "Address", 'XXX-XX-' + Substring(Convert(varchar(9), SSN), 6, 4) as "Social Security #" FROM Users

または、すべてを 1 行に詰め込みます。

于 2009-04-19T03:53:16.733 に答える
21
select some_column, ...
from some_table
group by some_column

結果がsome_columnでソートされると仮定します。仮定が成り立つ Sybase でこれを少し見てきました (今のところ)。

于 2008-12-06T22:25:41.080 に答える
17
  • FROM TableA, TableB WHEREではなく、JOINSの構文FROM TableA INNER JOIN TableB ON

  • ORDER BY 句を挿入せずに特定の方法で並べ替えられたクエリが返されるという仮定を作成します。これは、クエリ ツールでのテスト中に表示された方法であったためです。

于 2008-12-06T23:03:39.710 に答える
15

キャリアの最初の 6 か月で SQL を学び、次の 10 年間は他のことをまったく学びません。特に、ウィンドウ処理/分析 SQL 機能を学習したり、効果的に使用したりしていません。特に、over() と partition by の使用。

ウィンドウ関数は、集計関数と同様に、定義された行のセット (グループ) に対して集計を実行しますが、グループごとに 1 つの値を返すのではなく、グループごとに複数の値を返すことができます。

ウィンドウ関数の概要については、O'Reilly SQL Cookbook Appendix Aを参照してください。

于 2010-02-03T17:59:56.677 に答える
13

リストを完成させるために、私自身の現在のお気に入りをここに置く必要があります。私のお気に入りのアンチパターンは、クエリをテストしないことです

これは次の場合に適用されます。

  1. クエリに複数のテーブルが含まれています。
  2. クエリの最適な設計ができたと思いますが、仮定をテストする必要はありません。
  3. 機能する最初のクエリを受け入れますが、それが最適化に近いかどうかについての手がかりもありません。

また、非定型または不十分なデータに対して実行されるテストはカウントされません。ストアド プロシージャの場合は、テスト ステートメントをコメントに入れて、結果と共に保存します。それ以外の場合は、結果とともにコード内のコメントに入れます。

于 2008-12-07T02:57:51.407 に答える
11

一時テーブルの乱用。

具体的には、このようなこと:

SELECT personid, firstname, lastname, age
INTO #tmpPeople
FROM People
WHERE lastname like 's%'

DELETE FROM #tmpPeople
WHERE firstname = 'John'

DELETE FROM #tmpPeople
WHERE firstname = 'Jon'

DELETE FROM #tmpPeople
WHERE age > 35

UPDATE People
SET firstname = 'Fred'
WHERE personid IN (SELECT personid from #tmpPeople)

必要のない行を削除するためだけに、クエリから一時テーブルを作成しないでください。

はい、本番データベースでこの形式のコードのページを見たことがあります。

于 2009-04-20T22:05:53.150 に答える
9

逆張りの見方: 正規化への過度の執着。

ほとんどの SQL/RBDB システムは、正規化されていないデータでも非常に役立つ多くの機能 (トランザクション、レプリケーション) を提供します。ディスク容量は安価であり、フェッチされたデータを操作/フィルタリング/検索する方が、1NF スキーマを作成し、その中のすべての手間 (複雑な結合、厄介なサブセレクト) を処理するよりも簡単な場合があります (コードが簡単になり、開発時間が短縮されます)。など)。

過度に正規化されたシステムは、特に開発の初期段階では、最適化が時期尚早であることが多いことがわかりました。

(それについてのより多くの考え... http://writeonly.wordpress.com/2008/12/05/simple-object-db-using-json-and-python-sqlite/

于 2008-12-12T17:55:35.857 に答える
8

コメントのないストアド プロシージャまたは関数...

于 2011-10-12T12:02:41.257 に答える
7

1)それが「公式の」アンチパターンかどうかはわかりませんが、データベース列の魔法の値として文字列リテラルを嫌い、避けようとしています。

MediaWiki のテーブル 'image' の例:

img_media_type ENUM("UNKNOWN", "BITMAP", "DRAWING", "AUDIO", "VIDEO", 
    "MULTIMEDIA", "OFFICE", "TEXT", "EXECUTABLE", "ARCHIVE") default NULL,
img_major_mime ENUM("unknown", "application", "audio", "image", "text", 
    "video", "message", "model", "multipart") NOT NULL default "unknown",

(私は別のケーシング、避けるべき別のことに気づきました)

私は、int 主キーを持つテーブル ImageMediaType および ImageMajorMime への int ルックアップなどのケースを設計します。

2) 特定の NLS 設定に依存する日付/文字列変換

CONVERT(NVARCHAR, GETDATE())

フォーマット識別子なし

于 2008-12-06T20:05:19.133 に答える
7
  • The Altered View - 通知や理由なく頻繁に変更されるビュー。変更は、最も不適切なタイミングで気付くか、さらに悪いことに、間違っていてまったく気付かないかのいずれかです。誰かがその列のより良い名前を考えたために、アプリケーションが壊れる可能性があります。原則として、ビューは、消費者との契約を維持しながら、ベース テーブルの有用性を拡張する必要があります。問題を修正しますが、機能を追加したり、動作を悪化させたりしないでください。そのためには、新しいビューを作成します。軽減するには、ビューを他のプロジェクトと共有せず、プラットフォームが許可する場合はCTEを使用します。ショップに DBA がいる場合、おそらくビューを変更することはできませんが、その場合、すべてのビューが古くなって役に立たなくなります。

  • !Paramed - クエリには複数の目的がありますか? おそらくですが、次にそれを読む人は、深く瞑想するまでわからないでしょう。デバッグするのが「ただ」であっても、今はそれらを必要としない可能性があります。パラメータを追加すると、メンテナンス時間が短縮され、物事が DRY に保たれます。where句がある場合は、パラメータが必要です。

  • ケースがない場合のケース -

    SELECT  
    CASE @problem  
      WHEN 'Need to replace column A with this medium to large collection of strings hanging out in my code.'  
        THEN 'Create a table for lookup and add to your from clause.'  
      WHEN 'Scrubbing values in the result set based on some business rules.'  
        THEN 'Fix the data in the database'  
      WHEN 'Formating dates or numbers.'   
        THEN 'Apply formating in the presentation layer.'  
      WHEN 'Createing a cross tab'  
        THEN 'Good, but in reporting you should probably be using cross tab, matrix or pivot templates'   
    ELSE 'You probably found another case for no CASE but now I have to edit my code instead of enriching the data...' END  
    
于 2008-12-08T01:46:21.050 に答える
7

クエリ内の同一のサブクエリ。

于 2008-12-06T20:15:13.683 に答える
5

SQL アプリケーション (個々のクエリとマルチユーザー システムの両方) を高速または低速にする理由をよく理解せずにクエリを作成する開発者。これには、以下に関する無知が含まれます。

  • ほとんどのクエリのボトルネックが CPU ではなく I/O であることを考えると、物理 I/O 最小化戦略
  • さまざまな種類の物理ストレージ アクセスのパフォーマンスへの影響 (たとえば、大量のシーケンシャル I/O は多数の小さなランダム I/O よりも高速ですが、物理ストレージが SSD の場合はそれほど速くはありません!)
  • DBMS が不適切なクエリ プランを生成する場合にクエリを手動で調整する方法
  • データベースのパフォーマンスの低下を診断する方法、遅いクエリを「デバッグ」する方法、クエリ プランを読み取る方法 (選択した DBMS によっては EXPLAIN)
  • スループットを最適化し、マルチユーザー アプリケーションでのデッドロックを回避するためのロック戦略
  • データセットの処理を処理するためのバッチ処理およびその他のトリックの重要性
  • スペースとパフォーマンスのバランスを最適化するためのテーブルとインデックスの設計 (たとえば、インデックスをカバーする、インデックスを可能な限り小さく保つ、データ型を必要最小限のサイズに減らすなど)。
于 2011-11-24T05:33:45.537 に答える
5

私が最もよく見つけ、パフォーマンスの面でかなりのコストがかかる可能性があるのは次の 2 つです。

  • セットベースの式の代わりにカーソルを使用します。これは、プログラマーが手順を追って考えているときによくあることだと思います。

  • 相関サブクエリを使用して、派生テーブルへの結合がジョブを実行できる場合。

于 2008-12-07T04:48:27.923 に答える
5

一時テーブルに何かを入れる、特に SQL Server から Oracle に切り替える人は、一時テーブルを過剰に使用する習慣があります。ネストされた select ステートメントを使用するだけです。

于 2009-01-01T13:11:28.373 に答える
4

主キーをレコード アドレスの代理として使用し、外部キーをレコードに埋め込まれたポインタの代理として使用します。

于 2010-09-15T13:01:16.897 に答える
3

アプリケーションの結合SQLの問題だけでなく、問題の説明を探してこの質問を見つけたところ、リストに載っていないことに驚きました。

使用されているフレーズを聞いたように、アプリケーション結合とは、2つ以上のテーブルのそれぞれから行のセットを引き出し、ネストされたforループのペアを使用して(Java)コードでそれらを結合することです。これは、クロス積全体を識別し、それを取得してアプリケーションに送信する必要があるため、システム(アプリとデータベース)に負担をかけます。アプリがクロス積をデータベースと同じ速さでフィルタリングできると仮定すると(疑わしい)、結果セットをより早く削減するだけで、データ転送が少なくなります。

于 2011-11-17T16:02:50.783 に答える
3

SQL を美化された ISAM (Indexed Sequential Access Method) パッケージとして使用する。具体的には、SQL ステートメントを 1 つのステートメントに結合するのではなく、カーソルをネストします。実際にはオプティマイザーができることはあまりないため、これも「オプティマイザーの悪用」としてカウントされます。これを準備されていないステートメントと組み合わせて、最大の非効率性を実現できます。

DECLARE c1 CURSOR FOR SELECT Col1, Col2, Col3 FROM Table1

FOREACH c1 INTO a.col1, a.col2, a.col3
    DECLARE c2 CURSOR FOR
        SELECT Item1, Item2, Item3
            FROM Table2
            WHERE Table2.Item1 = a.col2
    FOREACH c2 INTO b.item1, b.item2, b.item3
        ...process data from records a and b...
    END FOREACH
END FOREACH

正しい解決策 (ほとんどの場合) は、2 つの SELECT ステートメントを 1 つに結合することです。

DECLARE c1 CURSOR FOR
    SELECT Col1, Col2, Col3, Item1, Item2, Item3
        FROM Table1, Table2
        WHERE Table2.Item1 = Table1.Col2
        -- ORDER BY Table1.Col1, Table2.Item1

FOREACH c1 INTO a.col1, a.col2, a.col3, b.item1, b.item2, b.item3
    ...process data from records a and b...
END FOREACH

二重ループ バージョンの唯一の利点は、内側のループが終了するため、Table1 の値の間の区切りを簡単に見つけられることです。これは、コントロール ブレーク レポートの要因になる可能性があります。

また、アプリケーションでの並べ替えは、通常はノーノーです。

于 2008-12-06T22:29:16.783 に答える
3

私はあまりにも多くの人々IN (...)EXISTS. 良い例については、Symfony Propel ORM を参照してください。

于 2011-08-10T07:35:47.357 に答える
3

次のようなビュー定義に出くわしました:

CREATE OR REPLACE FORCE VIEW PRICE (PART_NUMBER, PRICE_LIST, LIST_VERSION ...)
AS
  SELECT sp.MKT_PART_NUMBER,
    sp.PRICE_LIST,
    sp.LIST_VERSION,
    sp.MIN_PRICE,
    sp.UNIT_PRICE,
    sp.MAX_PRICE,
...

ビューには 50 ほどの列があります。一部の開発者は、列のエイリアスを提供しないことで他の人を苦しめることに少し誇りを持っているため、ビュー内のどの列に対応するかを把握できるようにするために、両方の場所で列のオフセットをカウントする必要があります。

于 2011-05-05T23:42:50.503 に答える
2

テーブルが1つある

code_1
value_1
code_2
value_2
...
code_10
value_10

3つのテーブルを持つ代わりに

code、value、code_value

あなたはあなたが10以上のカップルのコード、価値をいつ必要とするかもしれないかを決して知りません。

必要なカップルが1つだけの場合でも、ディスクスペースを無駄にすることはありません。

于 2009-05-12T20:13:47.083 に答える
2

re:SCOPE_IDENTITY()の代わりに@@IDENTITYを使用

どちらも使用しないでください。代わりに出力を使用してください

cf. https://connect.microsoft.com/SQLServer/feedback/details/328811/scope-identity-sometimes-returns-incorrect-value

于 2010-08-30T10:27:21.263 に答える
2

次のようなクエリに冗長なテーブルを結合します。

select emp.empno, dept.deptno
from emp
join dept on dept.deptno = emp.deptno;
于 2009-01-14T17:17:16.433 に答える
2

私のお気に入りの SQL アンチパターン:

JOIN一意でない列でSELECT DISTINCT、結果をトリミングするために使用します。

1 つのテーブルからいくつかの列を選択するためだけに、多くのテーブルを結合するビューを作成します。

 CREATE VIEW my_view AS 
     SELECT * FROM table1
     JOIN table2 ON (...)
     JOIN table3 ON (...);

 SELECT col1, col2 FROM my_view WHERE col3 = 123;
于 2011-04-04T18:39:28.473 に答える
1

おそらくアンチパターンではないかもしれませんが、特定のDBのDBA(ここではOracleについて話しています)がOracleスタイルとコード規則を使用してSQL Serverコードを記述し、実行が非常に悪いと文句を言うと、私はイライラします。カーソルのオラクルの人々で十分です!SQL はセットベースであることを意図しています。

于 2009-01-01T13:05:54.813 に答える
0

With 句または適切な結合を使用せず、サブクエリに依存しています。

アンチパターン:

select 
 ...
from data
where RECORD.STATE IN (
          SELECT STATEID
            FROM STATE
           WHERE NAME IN
                    ('Published to test',
                     'Approved for public',
                     'Published to public',
                     'Archived'
                    ))

より良い:
with 句を使用して意図を読みやすくするのが好きです。

with valid_states as (
          SELECT STATEID
            FROM STATE
           WHERE NAME IN
                    ('Published to test',
                     'Approved for public',
                     'Published to public',
                     'Archived'
                    )
select  ... from data, valid_states
where data.state = valid_states.state

一番:

select 
  ... 
from data join states using (state)
where 
states.state in  ('Published to test',
                     'Approved for public',
                     'Published to public',
                     'Archived'
                    )
于 2010-02-03T18:19:20.453 に答える