1

mysql で価格のタイムトラベル ビューを実装する必要があります。基本価格表は次のとおりです。

CREATE TABLE product_price (
  product_id INT(11) NOT NULL,
  date_valid TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  price DECIMAL(15,4) NOT NULL,
  PRIMARY KEY(product_id,date_valid)
);

時間の経過とともに、事前に入力した正しい有効な価格を選択するという考え方です。たぶん、コンセプトは後でより明確になる可能性があります。各 product_id について最新の価格を取得できるように、ビューを作成する必要があります。しばらくすると、必要なことを行う SELECT が見つかりました。

SELECT * FROM  (
  SELECT product_id,price FROM product_pric
    WHERE date_valid <= CURRENT_TIMESTAMP
    ORDER BY date_valid DESC
) xx GROUP BY product_id;

必要なビューを作成するには、副選択を使用できず、1 つ以上の中間ビューを作成する必要があることを理解しました。このような:

CREATE VIEW v_product_price_time AS
  SELECT product_id,price FROM product_pric
    WHERE date_valid <= CURRENT_TIMESTAMP
    ORDER BY date_valid DESC
;


CREATE VIEW v_product_price AS
  SELECT * FROM v_product_price_time GROUP BY product_id;

私が得たものは、私が書いた元のクエリと同じではありません。たとえば、テーブルに 2 つの行だけを入力します。

INSERT INTO product_price (product_id,date_valid,price ) VALUES ( 1,'2013-01-01',41.40 );
INSERT INTO product_price (product_id,date_valid,price ) VALUES ( 1,'2013-01-03',42.0 );

生のクエリは正しいデータ (1,42.0) を返しますが、ビューのクエリはそうではありません。私はいつも (1,41.40) を取得します。

私はMySQLをよく知らないので、確かに何かが欠けています。別のオープンソース RDBMS を使用して、すでに同様の作業を行っていますが、現在は MySQL v5.5 に対処する必要があり、変更する方法がありません。しかし、ドキュメントと開発者フォーラムでのいくつかの検索では、解決策にたどり着きませんでした。これを解決する方法について何か考えはありますか?ティア。

4

2 に答える 2

1

ビューにあるかどうかに関係なく、このクエリを使用します。

SELECT p1.* FROM (
  SELECT * FROM product_price
  WHERE date_valid <= CURRENT_TIMESTAMP
) p1
LEFT JOIN (
  SELECT product_id, date_valid FROM product_price
  WHERE date_valid <= CURRENT_TIMESTAMP
) p2
ON p1.product_id = p2.product_id AND p1.date_valid < p2.date_valid
WHERE p2.date_valid IS NULL

クエリは 2 つの派生テーブルを作成しますが、これはあまり効率的ではなく、読みにくいものでもあります。そのために別のビューを作成してみることができます。

CREATE VIEW product_price_past_dates AS (
  SELECT * FROM product_price
  WHERE date_valid <= CURRENT_TIMESTAMP
);

そして、元のクエリを次のように書き換えます。

SELECT p1.* FROM product_price_past_dates p1
LEFT JOIN product_price_past_dates p2
ON p1.product_id = p2.product_id AND p1.date_valid < p2.date_valid
WHERE p2.date_valid IS NULL

次に、前のビューを使用するクエリでビューを作成できます。

CREATE VIEW v_product_price_time AS (
  SELECT p1.* FROM product_price_past_dates p1
  LEFT JOIN product_price_past_dates p2
  ON p1.product_id = p2.product_id AND p1.date_valid < p2.date_valid
  WHERE p2.date_valid IS NULL
);

そして、最も単純なクエリで終了します。

SELECT * FROM v_product_price_time;

ここでフィドル。

GROUP BY が機能しない理由:基本的に、エラーは GROUP BY 句の不適切な使用に存在します。経験則では (100% の場合はそうではありませんが)、select では GROUP BY と同じフィールドを常に使用します。それ以外の場合、MySQL は GROUP BY ではなく select にあるフィールドから任意の値を選択します。

詳細については、MySQL のドキュメントを確認してください。かなり明確だと思います。

非常に詳細な説明:

SELECT * FROM  (
  SELECT product_id,price FROM product_pric
    WHERE date_valid <= CURRENT_TIMESTAMP
    ORDER BY date_valid DESC
) xx GROUP BY product_id;

0 syntactical errors
1 semantical error
1 warning
  1. セマンティカル エラー: product_id と price を選択し、product_id だけでグループ化すると、価格ごとに予測できない価格が返されます。結果セットに予測できない値を含めたくありません。したがって、予測できない値を 100% 信頼していることになります。それはさすがにエラー
  2. 警告: 結果セットを並べ替えて、GROUP BY 内にラップすることでその順序を取り除きます。何かを注文してから別の注文を生成しても意味がありません。これにより、パフォーマンスが低下します。

前のクエリは、2 つの基本的なグループごとの最大 n ソリューションのいずれかを使用して修正する必要があります。左結合である最短のものを提供しました。

CREATE VIEW v_product_price_time AS
  SELECT product_id,price FROM product_pric
    WHERE date_valid <= CURRENT_TIMESTAMP
    ORDER BY date_valid DESC
;

0 syntactical errors
0 semantical errors
0 warnings

完全に有効なクエリ。コメントは一切ありません。

CREATE VIEW v_product_price AS
  SELECT * FROM v_product_price_time GROUP BY product_id;

0 syntactical errors
1 semantical errors
0 warnings
  1. 意味論的エラー: ここでも、product_id と price を選択し、product_id によるグループ化のみを行っています。これは、上記と同じように、予測不能な結果を​​もたらします。

したがって、基本的には、2 つの予測不可能な結果を​​比較し、同じ結果になることを期待しています。おもしろいことに、2 つの予測できない結果を比較すると、1 つの予測できない結果よりもエラーが発生しやすくなります。したがって、コード内でこのエラーを見つけるチャンスが増えたことを幸運に思ってください。この質問をした人:あるフィールドの値を別のフィールドの最も頻繁に使用される値に更新するにはどうすればよいですか?

クエリが期待どおりに機能していないことに気付いたとき、面白い驚きを発見します。さらに、そこにある 4 つの回答のうち 3 つが正しくグループ化されておらず、予測できない結果を返しています。彼が自分のコードを述べているボヘミアンのコメントは言うまでもありませんwill always work。おめでとうございます、エンツォ、0 で割りました :)

お役に立てれば。

于 2013-09-05T05:58:01.070 に答える
0

これが私がそれを行う方法です:

SELECT *
FROM product_price pp
INNER JOIN (
  SELECT product_id,MAX(date_valid) most_recent_date
  FROM product_price pp
  WHERE date_valid <= CURRENT_TIMESTAMP
  GROUP BY product_id
) aux ON aux.product_id = pp.product_id
AND aux.most_recent_date = pp.date_valid

サブクエリでは、product_id とともに各 product_id の最新の有効な日付を選択します。これにより、product_id (日付が有効で、現在のタイムスタンプ以下) ごとに 1 つの行が得られます。product_id と date_valid (PK) を取得したので、これらの行を product_price テーブルと安全に結合して、残りのデータを取得できます。この「トリック」は、サブクエリでグループ内の 1 つの行を選択するためのソリューションとして常に機能するとは限りませんが、各製品の最新の日付を取得する必要があるため、MAX 関数を利用できます。

必要に応じて、サブクエリをビューに配置できます。

CREATE VIEW most_recent_product AS (
  SELECT product_id,MAX(date_valid) most_recent_date
  FROM product_price pp
  WHERE date_valid <= CURRENT_TIMESTAMP
  GROUP BY product_id
)

その後:

SELECT *
FROM product_price pp
INNER JOIN most_recent_product aux ON aux.product_id = pp.product_id
AND aux.most_recent_date = pp.date_valid

これがフィドルです

于 2013-09-07T16:42:05.820 に答える