データベースの日付データ型の十字軍について:有効ですか?価値がある?他の誰かがそれを感じますか?


13

SOに関するSQLの質問に答えるのに多くの時間を費やしています。私はこのilkのクエリに頻繁に出くわします:

SELECT * FROM person WHERE birthdate BETWEEN '01/01/2017' AND '01/03/2017'

SELECT * FROM person WHERE birthdate BETWEEN '2017-01-01' AND '2017-03-01'

SELECT * FROM person WHERE birthdate BETWEEN 'some string' AND 'other string'

つまり、指定されたパラメータの文字列から日付への暗黙的な変換(不良)に依存するか、データベースがx、000,000個のデータベース行値を文字列に変換し、文字列比較を実行します(悪い)

私は時々コメントをします。特に、スマートな回答を書くのが高回答ユーザーである場合、私は本当に自分のデータ型でだらしない/文字列を入力するべきではないと思う人

コメントは通常、to_date(Oracle)、str_to_date(MySQL)、convert(SQLSERVER)、または同様のメカニズムを使用して、文字列を明示的に日付に変換した方が良いと思われる形式を取ります。

    --oracle
    SELECT * FROM person WHERE birthdate BETWEEN TO_DATE('20170101', 'YYYYMMDD') AND TO_DATE('20170301', 'YYYYMMDD')

    --mysql
    SELECT * FROM person WHERE birthdate BETWEEN STR_TO_DATE('20170101', '%Y%m%d') AND STR_TO_DATE('20170301', '%Y%m%d')

    --SQLS, ugh; magic numbers
    SELECT * FROM person WHERE birthdate BETWEEN CONVERT(datetime, '20170101', 112) AND CONVERT(datetime, '20170301', 112)

これを行うための技術的な理由は、日付の形式が明示的であり、いくつかのソースパラメーターが確実にターゲット列のデータ型になることです。これにより、データベースが暗黙的な変換を誤ってしまう可能性を防ぎ(最初の例の1月3日/ 1月3日の引数)、dbがテーブル内の100万の日付値を文字列に変換することを決定します(サーバー固有の日付を使用して)比較を行うために、sql内の文字列パラメーターの日付の形式と一致しない可能性があるフォーマット-ホラーがたくさん

そうすることに対する私の社会的/学術的正当性は、SOが学習サイトであるということです。暗黙的または明示的に知識を習得します。このクエリを答えとして初心者をヒットするには:

SELECT * FROM person WHERE birthdate BETWEEN '2017-01-01' AND '2017-03-01'

これは賢明だと思うように彼らを導くかもしれません。

SELECT * FROM person WHERE birthdate BETWEEN '01/01/2017' AND '01/03/2017'

少なくとも日付を変換する明示的な試みを見た場合は、奇妙な日付形式でそれを実行し、発生する前にいくつかの永遠のバグを殺すかもしれません。結局、私たちは、人々がSQLインジェクションの習慣にならないようにしようとしています(そして@pBirthdate、フロントエンドにdatetime型がある場合、クエリをパラメーター化してから、文字列であるドライバーに宣言することを推奨しますか?)

推奨事項を作成した後に戻る:通常、「明示的に、xを使用」推奨事項へのプッシュバックを取得します。それは私が明示的であるべきだと言っている」あるいは「何?」

これらのいくつかに応えてWHERE age = '99'、年齢を文字列として渡すことでint列を検索するかどうかを尋ねました。「愚かなことをしないで、「intを検索するとき」を入力する必要はありません。そのため、どこかでさまざまなデータ型を認識しているかもしれません。文字列を渡すことによる(明らかに愚かな)列と文字列を渡すことによる(明らかに賢明な)日付列の検索は偽善的です

したがって、SQLには、数値(区切り記号なしの数値を使用)、文字列文字列(アポストロフィ区切り記号の間にあるものを使用)として物事を書く方法があります。ほとんどのDBでこれは基本的なデータ型ですか?javascript /がいくつかの文字のどちらかの側を置くことで正規表現を指定できるように、日付を書く方法を持っているだけで、この全体を解決できるかもしれません。/Hello\s+world/。デート用に何か持ってみませんか?

実際、私の知る限り、Microsoft Accessには「これらの区切り記号の間に日付が書き込まれている」ことを示す記号が実際にあるため、次のような良いショートカットを取得できますWHERE datecolumn = #somedate#が、日付の表示には問題があります:mm / di vs dd / mm、MSは常にVBの観客が良いアイデアだと思ったもので、速くてゆるいプレイをしてきたからです。


要点に戻ります:多数の異なるデータ型を文字列として渡すことを強制するこの媒体を明示するのが賢明であると主張しています。

有効なアサーションですか?

この十字軍を続けるべきですか?文字列型入力が現代のノーノーであることは有効なポイントですか?または、すべてのRDBMS(古代バージョンを含む)が存在する場合、クエリを実行WHERE datecolumn = 'string value'すると、文字列が確実に正しく日付に変換され、テーブルデータの変換/インデックスの使用を失うことなく検索が実行されますか?少なくとも、Oracle 9の個人的な経験からは、そうではないと思います。文字列が常にISO標準形式で記述されていて、列が何らかの日付フレーバーである場合、逃げるシナリオがあるかもしれません。文字列パラメーターは常に正しく暗黙的に変換されます。これは正しいですか?

やりがいのある仕事ですか?

多くの人はそれを取得していないようであるか、気にしないか、またはintがintであるにもかかわらず日付が文字列であるという偽善を示します。ほとんどの人に共通しているのは、なに、あなたの意見に同意します。これからの日付について明確にします。」


私は誰かがWHERE datecolumn = 01/02 / 12'`で問題を抱えているのを見たことがありますが、1912、2012、2001、1901、12または1年を求めている可能性があります。変換理由を理解することはできませんプログラマーの"09"クラッシュを引き起こしているint型にしている軍団、9が有効進数字ではなく、先頭の0は、多くのシステムでは、文字列進を作る
スティーブ・バーンズ

2
私はかどうかを尋ねるために私の例を拡張について考えましたWHERE age = '0x0F'。..データベースは、15歳の検索を願っする有効な方法である
キーズJard

1
ここでトピック外の質問を削除しました-リソース要求は行いません。この理由で、2つの投票のうち1つが行われました。さもなければ、これは妥当な質問だと思いますが、あまりにも広すぎることに国境を接するかもしれません。トピック外の質問を削除することで、少し物事を絞り込むことができれば幸いです。
トーマスオーエンズ

TL; DRですが、実稼働システムでは、このような日付はほとんど常にパラメーターに含まれると予想されます。クエリへの日付のハードコーディングは、暗黙的な変換を使用するかどうかよりも大きな問題です。スローアウェイクエリを書いている場合、動作するかしないかのどちらかです。とにかくこれを行うことはありません(デフォルトの日付形式を思い出せないため)
ジミージェームズ

1
人生はあなたの戦いを選ぶことです。私の見解では、これはただの戦い...価値がない
ロビーディー

回答:


7

あなたが書いた:

これらのパラメータは、1月1日から1月3日、または3月1日です。

それは確かに潜在的なエラーの原因です。質問者にこれを指摘することは、他の読者にとって役立つかもしれないので、はい、これは有効な懸念事項です。しかし、建設的であるために、私は

  • ANSI SQLを参照し、その標準のDATEまたはDATETIMEリテラルを使用します

  • 特定のDBMSの通常の明確な日時形式を使用します(使用されているSQL方言に言及します)

残念ながら、すべてのDBMSがANSI SQL日付リテラルをまったく同じ方法でサポートしているわけではありません(まったくサポートしている場合)。そのため、通常は2番目のアプローチのバリアントになります。「標準」が異なるDBベンダーによって厳密に実装されていないという事実は、おそらくここでの問題の一部です。

さらに、多くの現実世界のシステムでは、クライアントアプリケーションがローカライズされている場合でも、常に同じように構成された1種類のサーバーしかないため、人々は実際にデータベースサーバー上の特定の固定ロケールに依存できます。そのため、「2017年3月1日」は、作業対象の特定のシステムで使用されるSQLの固定形式「dd / mm / yyyy」または「mm / dd / yyyy」であると想定されることがよくあります。だから誰かが「それはいつも私のために働く」とあなたに言ったら、これはおそらく彼の環境にとって賢明な答えかもしれません。この場合、このトピックについて議論する価値はなくなります。

「パフォーマンスの理由」について話す:測定可能なパフォーマンスの問題がない限り、これは「潜在的なパフォーマンスの問題」と議論するのはかなり迷信的です。データベースが100万文字列から最新の変換を行っているか、時間差が1000分の1秒である場合はおそらく問題ではなく、実際のボトルネックはクエリが10秒続くネットワークです。誰かがパフォーマンスの考慮事項を明示的に求めている限り、これらの懸念は脇に置いておく方が良いでしょう。

この十字軍を続けるべきですか?

秘密を教えてください。宗教戦争は嫌いです。それらは有用なものにはつながりません。そのため、SQLのあいまいな日付/時刻の仕様が問題を引き起こす可能性がある場合は、それらに言及しますが、現在のコンテキストで実際にメリットがもたらされない場合は、人々をより厳しくさせないでください。


ただし、これはアメリカの日付形式と賢明な日付形式のあいまいさについてはそれほど問題ではありません。SQLステートメントで日付を文字列として渡し、日付への暗黙的な変換に依存することが賢明かどうかについてです。データベースが100万行すべてに対して100万の日付-> str変換を行わなければならないという問題は、パフォーマンスの側面の1つであり、1つのクエリに対して1/1000秒しかかからないかもしれませんが、ユーザー。大きなパフォーマンスの問題は、データ変換手段のことであるインデックスが使用できなくなり、それは本当に深刻なことができる
キーズJard

@CaiusJard:私の答えは立っています:それは時々賢明であり、時々そうではありません、それは文脈に依存します。そして正直なところ、私はここで何かを「...想像...」することを拒否します。パフォーマンスに関しては、仮想的なケースを議論することは役に立ちません。測定可能なパフォーマンスの問題がある場合は、事前にではなく、最適化するとき、場合によっては微最適化するときです。
Doc Brown

仮説とみなすのは興味深いことです。私は(よく文書化の理由のために:列全体のデータが変換されている場合、それが検索される前に、インデックスは仕事をしません)に発生するバグやパフォーマンスの合併症のための明確な機会として、暗黙的な行動に頼る見て、明示的に指示してこれらが起こることはできません
キーズJard

@CaiusJard:言葉で遊ばないでください-「仮説」とは「ありそうもない」という意味ではありません。
Doc Brown

1
@CaiusJard:他の業界の専門家に感銘を与えたい場合は、「パフォーマンス最適化」が「セキュリティ最適化」と非常に異なる理由を正確に知っておく必要があります。これがまさにここでのポイントです。遅すぎる。セキュリティの問題ではなく、発生する前に徹底的に回避する必要があります。リンゴとオレンジを比較しないでください。あなたが十字軍が好きなら、セキュリティの議論はこれにはるかに適しています;
Doc Brown

5

あなたの十字軍は問題を解決しません。

次の2つの問題があります。

  • SQLの暗黙的な型変換

  • 07/06/07のようなあいまいな日付形式

私はあなたが十字軍でどこから来たのかわかりましたが、明示的な変換が実際に目の前の問題を解決するとは思いません。

  • 比較の型が一致しない場合、暗黙的な変換が引き続き発生します。文字列が日付と比較される場合、SQLは最初に文字列を日付に変換しようとします。したがって、日付型の列を明示的に変換された日付値と比較することは、文字列形式の日付と比較することとまったく同じです。私が見る唯一の違いは、日付値を実際には日付ではなく文字列を含む列と比較する場合です-しかし、これはいずれにしてもエラーになります。

  • 明示的な変換を使用しても、非ISO日付形式のあいまいさは解決されません。

私が見る唯一の解決策:

  • 文字列型の列を非文字列値と比較しないでください。
  • ISOタイプの日付形式のみを使用してください。

そしてもちろん、文字列型の列に日付を格納しないでください。ただし、日付リテラルの明示的な変換はこれを妨げません。

間違いなく、暗黙的な変換はSQLの誤りでしたが、言語の設計方法を考えると、明示的な変換の利点はわかりません。とにかく暗黙的な変換は避けられず、コードの読み取りと書き込みが難しくなります。


本当です。おそらく、この観点から指摘する必要があります。最も賢明なことは、datecolumnオペランドとvalueオペランドが同じデータ型(文字列、日付など)であることを確認することです。私は、具体的にのみ、私は質問では、この勧告作るのですか知っているテーブルの列がDATETIMEとその例の答えは暗黙の型変換で文字列オペランドを使用している..です
キーズJard

この答えについては、私と一緒に何かが正しくありません。興味深い点をいくつか挙げていますが、結論は理想主義的だと思います。設計の観点から、はい、非ISO日付形式は人間の目にはあいまいですが、明示的な変換を使用する場合、構文的には構文解析器にあいまいではありません。同様に、日付を含む多くのETLプロセスが必要としようとしているいくつかのデータベースの日付形式に文字列の(ファイルのインポートの形で)の比較を。文字列と日付の比較を排除しようとすることは、私にとって非現実的です。
じめじめ

@DanK:ETLは別の問題です。CSVファイルなどからデータを読み取る場合、明らかにデータを文字列として処理し、型指定された値に明示的に解析する必要があります。しかし、それはOPが説明しているシナリオではありません。
ジャックB

それは簡単に私が説明しているポイントかもしれません。解析時に形式を明示的に宣言することを要求するcsvに格納された数字の文字列について特別なことはありません。彼らはそれを心配する必要はありません(またはDBが正常にすべての時間をそれを解析すること)を前提とする初心者をリード宣言日付形式、
キーズJard

@CaiusJard:これらは非常に異なるシナリオだと思います。通常のシナリオでSQLについて話すとき、列には適切な型があると仮定します。つまり、整数列は整数型、日付列はデータ型などです。テーブルに正しい型がない(つまり、日付を文字列として保存する)場合、深刻な問題が発生し、クエリで日付リテラルを明示的に変換しても保存されません(これが私のポイントです)。
ジャックB

3

何よりもまず、ポイントがあります。日付は文字列に入れないでください。データベースエンジンは複雑な獣であり、任意のクエリが与えられた場合にフードの下で正確に何が起こるかを100%確信することはありません。日付に変換すると、物事が明確になり、パフォーマンスが向上します。

だが

それは、ほとんどの人にとって解決するための余分な労力に値する問題ではありません。クエリで日付リテラルを使用するのが簡単な場合、自分の立場を守るのは簡単です。しかし、そうではありません。私は主にSQL Serverを使用しているため、日付を変換するための混乱は起きていません。

ほとんどの人にとって、パフォーマンスの向上はごくわずかです。「なぜそうです、ボスマンさん、私はこの単純なバグを修正するために余分な10分を費やしました(その構文は...特別なため、日付を変換する方法をグーグルで調べなければなりませんでした...)。しかし、余分な0.00001秒を節約しましためったに実行されないクエリ。」それは私が働いたほとんどの場所を飛ぶつもりはありません。

しかし、それはあなたが言う日付形式のあいまいさを取り除きます。繰り返しますが、多くのアプリケーション(会社の内部アプリケーション、地方自治体のものなど)については、実際には問題ではありません。そして、それが懸念されるアプリケーション(大規模、国際的、またはエンタープライズアプリケーション)の場合、UI /ビジネスレイヤーの懸念になるか、これらの企業は既にこれを知っているDBAのチームを持っています。TL / DR:国際化が懸念される場合、誰かが既にそれについて考えており、あなたが示唆するように既にやっています(または、そうでなければ問題を軽減しました)。

ならどうしよう?

あなたがそんなに傾いていると感じたら、良い戦いを続けてください。しかし、ほとんどの人が、これが心配するほど重要だと感じていなくても驚かないでください。重要な状況があるからといって、それがすべての人の状況であることを意味するわけではありません(そうでない可能性が高い)。そのため、技術的には正しいがより適切ではないが、実際には関係のないものに対して何らかの反論が寄せられても驚かないでください。


1

文字列として多数の異なるデータ型を渡すことを強制するこの媒体を明示するのが賢明だと主張しています。

「日付」が「in」文字列に渡されると仮定すると、はい。私はあなたがこれを行う権利があることに絶対に同意します。

ときである「01/04/07」?
* 1月4日?
* 4月1日?
* 4月7日[2001]?

「コンピューター」がそれらを解釈する方法に応じて、これらのいずれかまたはすべてが正しい場合あります。

リテラルを使用して動的SQLを構築する必要がある場合、日付の書式設定を適切に定義し、できればマシンに依存しないようにする必要があります(Windowsサービスでの日付ベースの処理がうまくいかないWindowsサーバーで奇妙なものがありました)オペレータが異なる日付形式の設定でコンソールにログオンしたためです!)。個人的には、「yyyy-mm-dd」形式のみを使用します[d]。

しかしながら ...

最善の解決策は、データ型を変換する力パラメータ化クエリを使用することであるに「日付」の値を取得- SQLが関与取得を日付パラメータ(それは純粋にコーディングの問題ではなく、SQLの1にする)に初期の型変換を強制的に。


私は同意しますが、同じ問題はパラメーター化されたクエリWHERE datecolumn = @dateParameterで、フロントエンドコードで実行し、@dateParametervarchar型のDBドライバーに伝え、"01/04/07"それに固執することで強化できます。私の質問の元のインスピレーションは、パラメータ化されたクエリに対してそれを行うことに夢中だと言う人は誰でも、同じ息の中でいくつかの行を答えるだろうと疑っていることですWHERE datecol = 'some string that looks like a date'(そして初心者は知っているはずです問題を回避するためのヒント/パラメータ化にすぎません)
Caius Jard
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.