9

MySQL のテーブル内の各レコードに対して選択された関連レコードを出力する方法を探しています。さらに説明します...

私は 2 つのテーブルcurrencyexchange_ratesを持っています。テーブルはcurrency_codeフィールドで結合され、各通貨レコードには関連する複数の為替レート レコードがあり、各為替レート レコードは異なる日を表します。したがって、通貨と exchange_rates の間には 1 対多の関係があります。

各通貨のテーブルから完全なレコードを取得したいのですexchange_ratesが、どの関連レコードを選択するかについて特定の基準を定義できます。各通貨の最新の exchange_rate だけでなくexchange_rates、フィールドを持つ各通貨の最新のレコードかもしれませんcriteria_x=NULL

派生テーブル内で使用できないのは残念です。LIMITそれ以外の場合、このようなものはきちんとした読みやすいソリューションになります...

SELECT `currencies`.`currency_code`, `currencies`.`country`, `exchange_rates`.`id`,
       FROM_UNIXTIME(`exchange_rates`.`datestamp`), `rate` 
FROM `currencies` 
INNER JOIN (
SELECT `id`, `currency_code`, `invoice_id`, `datestamp`, `rate` 
FROM `exchange_rates` 
WHERE `criteria_x`=NULL AND `criteria_y` LIKE 'A' 
ORDER BY `datestamp` DESC
LIMIT 0, 1
) AS `exchange_rates` ON `currencies`.`currency_code`=`exchange_rates`.`currency_code`
ORDER BY `currencies`.`country`

句は、LIMIT派生テーブルではなく、親クエリに適用されます。

これは私がこれを行うために見つけた唯一の方法です...

SELECT `currencies`.`currency_code`, `currencies`.`country`, 
FROM_UNIXTIME( SUBSTRING_INDEX( SUBSTRING_INDEX(`exchange_rates`.`concat`, '-', 1), '-', -1)) AS `datestamp`,
SUBSTRING_INDEX( SUBSTRING_INDEX(`exchange_rates`.`concat`, '-', 2), '-', -1) AS `id`, 
SUBSTRING_INDEX( SUBSTRING_INDEX(`exchange_rates`.`concat`, '-', 3), '-', -1) AS `rate` 
FROM `currencies`
INNER JOIN (
SELECT `currency_code`, MAX(CONCAT_WS('-', `datestamp`, `id`, `rate`)) AS `concat`
FROM `exchange_rates` 
WHERE `criteria_x`=NULL AND `criteria_y` LIKE 'A' 
GROUP BY `exchange_rates`.`currency_code`
) AS `exchange_rates` ON `currencies`.`currency_code`=`exchange_rates`.`currency_code`
ORDER BY `currencies`.`country`

したがって、一連のフィールドを連結し、それに対して を実行してMAX()、グループ内の並べ替え順序を取得し、親クエリでそれらのフィールドを解析しますSUBSTRING_INDEX()。問題は、このメソッドが連結フィールドでMIN()orを使用できる場合にのみ機能することです。MAX()文字列を並べ替えたり、複数の基準で並べ替えたりしたいが、単一のレコードに制限したい場合は理想的ではありません。

また、リレーショナル データベースから必要なデータを取得するために恐ろしい文字列操作に頼らなければならないのは、身体的な苦痛を引き起こします。もっと良い方法があるはずです。

より良い方法の提案はありますか?

4

4 に答える 4

4

回答を提供する前に、(簡単に) 説明する一般的な問題がいくつかあります。

最初のクエリは次のとおりです。

SELECT `currencies`.`currency_code`, `currencies`.`country`, `exchange_rates`.`id`,
       FROM_UNIXTIME(`exchange_rates`.`datestamp`), `rate` 
FROM `currencies` 
INNER JOIN (
SELECT `id`, `currency_code`, `invoice_id`, `datestamp`, `rate` 
FROM `exchange_rates` 
WHERE `criteria_x`=NULL AND `criteria_y` LIKE 'A' 
ORDER BY `datestamp` DESC
LIMIT 0, 1
) AS `exchange_rates` ON `currencies`.`currency_code`=`exchange_rates`.`currency_code`
ORDER BY `currencies`.`country`
  1. 使用しているほど多くの逆引用符を使用する必要はないと思います。それらは正確に間違っているわけではありませんが、回答に入力するつもりはありません。
  2. criteria_x = NULLSQL 標準はこの表記法を認めていません。と書くべきですcriteria_x IS NULL。MySQL が許可する場合があります。規格外ということを承知の上でご利用いただいても構いません。
  3. 基準LIKE 'A'にメタ文字が含まれていない場合 (%または_標準 SQL の場合)、基準は適切ではありません。単純な等式の方がよいでしょう: = 'A'

あなたの質問は言う:

各通貨のテーブルから完全なレコードを取得したいのですexchange_ratesが、どの関連レコードを選択するかについて特定の基準を定義できます。各通貨の最新の為替レートだけでなく、フィールドを持つ各通貨の最新の為替レートかもしれませんcriteria_x IS NULL

したがって、必要な他の基準を満たす各通貨の最新の為替レート レコードを選択する必要があります。為替レート表のcurrency_codeとの組み合わせには一意の制約があると想定できます。datestampこれは、一致する行が常に 1 つだけであることを意味します。一致する行がない場合に何を表示するかを指定していません。もちろん、内部結合は単にその通貨をリストしません。

SQL クエリでは、通常、クエリ全体を段階的に構築してテストし、以前に開発した、機能して適切な出力を生成することがわかっているクエリに追加の資料を追加します。単純な場合や思い込みが多すぎる場合は、最初に複雑なクエリを試しますが、(宿敵) うまくいかない場合は、ビルドとテストのプロセスに戻ります。テスト駆動 (クエリ) 開発と考えてください。

ステージ 1: 指定された条件に一致する為替レート レコード

SELECT id, currency_code, invoice_id, datestamp, rate 
  FROM exchange_rates 
 WHERE criteria_x IS NULL AND criteria_y = 'A' 
 ORDER BY currency_code, datestamp DESC

ステージ 2: 指定された条件に一致する各通貨の最新の為替レート時間

SELECT currency_code, MAX(datestamp) 
  FROM exchange_rates 
 WHERE criteria_x IS NULL AND criteria_y = 'A' 
 GROUP BY currency_code

ステージ 3: 指定された条件に一致する各通貨の最新の為替レート時間の為替レート レコード

SELECT x.id, x.currency_code, x.invoice_id, x.datestamp, x.rate 
  FROM exchange_rates AS x
  JOIN (SELECT currency_code, MAX(datestamp) AS datestamp
          FROM exchange_rates 
         WHERE criteria_x IS NULL AND criteria_y = 'A' 
         GROUP BY currency_code
       ) AS m
    ON x.currency_code = m.currency_code AND x.datestamp = m.datestamp

ステージ 4: 指定された基準に一致する各通貨の最新の為替レート時間の通貨情報と為替レート レコード

これには、通貨テー​​ブルを前のクエリの出力と結合する必要があります。

SELECT c.currency_code, c.country, r.id,
       FROM_UNIXTIME(r.datestamp), r.rate
  FROM currencies AS c 
  JOIN (SELECT x.id, x.currency_code, x.invoice_id, x.datestamp, x.rate 
          FROM exchange_rates AS x
          JOIN (SELECT currency_code, MAX(datestamp) AS datestamp
                  FROM exchange_rates 
                 WHERE criteria_x IS NULL AND criteria_y = 'A' 
                 GROUP BY currency_code
               ) AS m
            ON x.currency_code = m.currency_code AND x.datestamp = m.datestamp
       ) AS r
    ON c.currency_code = r.currency_code
 ORDER BY c.country

Oracleがテーブル エイリアスと の使用に) r' ' の代わりに ' 'のみを許可することを除いて、言及したいほぼすべての SQL DBMS の現在のバージョンで正しく動作するはずです。) AS rFROM_UNIXTIME()

最終クエリでは請求書 ID が返されないため、中間クエリの選択リストからそれを削除できます。優れたオプティマイザーはそれを自動的に行うかもしれません。

条件に一致する為替レートがなくても通貨情報を表示したい場合は、最も外側のクエリの JOIN を LEFT JOIN (別名 LEFT OUTER JOIN) に変更する必要があります。通貨のサブセットのみを表示する場合は、最後の (最も外側の) クエリ ステージでそのフィルターを適用できます。または (フィルターが通貨コードなど、為替レート テーブルで利用可能な情報に基づいている場合)最も内側のサブクエリ (最も効率的) または中間のサブクエリ (フィルタを最も内側のサブクエリにプッシュできることをオプティマイザが認識しない限り、それほど効率的ではありません)。

通常、正しさが第一の基準です。パフォーマンスは二次的な基準です。ただし、質問にはパフォーマンスが記載されていました。最初のルールは、ここに示す「単純な」クエリを測定することです。それが遅すぎることが判明した場合にのみ、さらに心配する必要があります。心配する必要がある場合は、クエリ プランを調べて、たとえば重要なインデックスが欠落していないかどうかを確認します。クエリがまだ十分に高速でない場合にのみ、他のトリックに頼ろうとし始めます。これらのトリックは、特定の DBMS に固有のものである傾向があります。たとえば、DBMS でクエリを別の方法で処理するために使用できるオプティマイザ ヒントが存在する場合があります。

于 2012-05-13T20:39:01.090 に答える
2

私があなたの問題を正しく理解していれば、自己参加exchange_ratesして利率を選択するだけです。

SELECT   currencies.currency_code,
         currencies.country,
         exchange_rates.id,
         FROM_UNIXTIME(exchange_rates.datestamp),
         exchange_rates.rate
FROM     currencies
  JOIN   (
    SELECT   currency_code, MAX(datestamp) AS datestamp
    FROM     exchange_rates
    WHERE    criteria_x IS NULL AND criteria_y LIKE 'A'
    GROUP BY currency_code
  )   AS exchange_wantd USING (currency_code)
  JOIN   exchange_rates USING (currency_code, datestamp)
ORDER BY currencies.country
于 2012-05-09T12:45:52.763 に答える
1

このクエリを試してください。正常に動作することが期待されますが、いくつかのデータを提供すると、適切に実行できるようになります

SELECT  `currencies`.`currency_code` as `CurrencyCode`,
    `currencies`.`country`, 
    FROM_UNIXTIME( SUBSTRING_INDEX( SUBSTRING_INDEX(`exchange_rates`.`concat`, '-', 1), '-', -1)) AS `datestamp`,
    SUBSTRING_INDEX( SUBSTRING_INDEX(`exchange_rates`.`concat`, '-', 2), '-', -1) AS `id`, 
    SUBSTRING_INDEX( SUBSTRING_INDEX(`exchange_rates`.`concat`, '-', 3), '-', -1) AS `rate`,
    (SELECT 
            MAX(CONCAT_WS('-', `datestamp`, `id`, `rate`)) AS `concat` 
            FROM `exchange_rates` 
            WHERE `criteria_x`= NULL 
            AND `criteria_y` LIKE 'A' 
            GROUP BY `exchange_rates`.`currency_code`
            HAVING `exchange_rates`.`currency_code` =`CurrencyCode`
    ) as `Concat`
FROM    `currencies`
ORDER BY `currencies`.`country` 
于 2012-05-04T06:11:21.363 に答える
0

私があなたを正しく理解していれば、データベースに人間のように思わせなければ、答えは非常に簡単です。ジョナサン・レフラーのように、「必要な他の基準を満たす各通貨の最新の為替レートの記録を選択する」というあなたの意図を理解しています。

もちろん、「最新」は問題です。その情報はDBに明示的に格納されていないため、最初にこの情報を判別します。

SELECT currency_code, MAX(datestamp) AS datestamp FROM exchange_rates GROUP BY currency_code

これを再利用するので、結果に名前を付けます

(SELECT currency_code, MAX(datestamp) AS datestamp FROM exchange_rates GROUP BY currency_code) AS dates_we_want

必要になる可能性のあるすべての情報を保持しているドメインは、dates_we_want、currency、exchange_ratesテーブルの可能な組み合わせごとのレコードです。

(SELECT currency_code, MAX(datestamp) AS datestamp FROM exchange_rates GROUP BY currency_code) AS dates_we_want, currencies AS c, exchange_rates AS er

必要なレコードの選択:

  • 一致するcurrency_codes

    days_we_want.currency_code = er.currency_core ANDdates_we_want.currency_code = c.currency_core

  • 最新の料金

    dates_we_want.datestamp = er.datestamp

結果への投影。君

exchange_ratesテーブルから完全なレコードを取得したい

単にに翻訳します

er.*

すべてをSELECTステートメントにまとめて、任意の制約を設定するための変更を加えます。

SELECT er.*
FROM
    (SELECT currency_code, MAX(datestamp) AS datestamp
       FROM exchange_rates GROUP BY currency_code
    ) AS dates_we_want,
    currencies AS c, exchange_rates AS er
WHERE
    dates_we_want.currency_code=er.currency_core
AND
    dates_we_want.currency_code=c.currency_core
AND
    dates_we_want.datestamp=er.datestamp
AND
    `criteria_x`=NULL AND `criteria_y` LIKE 'A' 
于 2012-05-15T13:34:17.393 に答える