web-dev-qa-db-ja.com

SQL:別の行に*条件*がある場合、行を更新します

[〜#〜] sql [〜#〜]に問題があります。

別の行の値が条件に一致する場合にのみ、特定の行の値を更新したい。次に、2行目の別の値を更新します。

この方法で説明するとわかりにくいので、コードを次に示します(mysqliを使用してパラメーターをバインドします)。

--mariaDB:

UPDATE `accountlist` JOIN `data` ON `accountlist`.`id`=`data`.`id`
    SET `upvotes`= `upvotes` + (`user`= ?),
        `allow` = (CASE WHEN `accountlist`.`id` = ?
                   THEN ?
                   ELSE `allow`
                   END)
WHERE (SELECT `allow` FROM `data` WHERE `id` = ?) < ?;

--mysql:

UPDATE `accountlist` JOIN `data` ON `accountlist`.`id`=`data`.`id`
    SET `upvotes`= `upvotes` + (`user`= ?),
        `allow` = (CASE WHEN `accountlist`.`id` = ?
                   THEN ?
                   ELSE `allow`
                   END)
WHERE `data`.`id` = ? AND `allow` < ?;

--params sample: "admin", 2, "2020-04-20", 2, "2020-04-20"
--               (same value here means always same value)

ローカルホストとホストに異なるデータベースがあるため、MySQLバージョンと非MySQLバージョンの両方を維持する必要があり、何らかの理由で最初のバージョンがMySQLで機能しません。

dataは、accountlistと1対1の関係にある別のテーブルです(同じidsの行数は常に同じです)。

とにかく、すべてを簡略化するために、user=?row 1である行とaccountlist.id=?row 2である行を呼び出します。

私がしたいことは:

  1. user on row 1 update allow on row 2?より小さい場合
  2. allowを更新行2allow on 行2?より小さい場合)

2番目のポイントの状態はそれ自体に更新されるため、それほど重要ではありませんが、それが私がそれを行うことができた唯一の方法です。

私の問題:

  • 最初のコード(MySQL以外)は、upvotes on row 1を更新しますが、allow on row 2は変更しません。
  • 2番目のコード(MySQL)は、arrow on row 2を更新しますが、user on row 1は変更しません。

独自のMySQL +非MySQLバージョンを意味する可能性のあるソリューションはありますか?



更新:

ここに例を示します:

     accountlist                       data
   |------------|---------------|    |------------|--------------|----------|
   | id         | user          |    | id         | allow        | upvotes  |
   |------------|---------------|    |------------|--------------|----------|
A: | 1          | admin         |    | 1          | 2020-04-18   | 2        |
B: | 2          | foo           |    | 2          | 2020-04-20   | 0        |
C: | 3          | bar           |    | 3          | 2020-04-22   | 1        |
   |------------|---------------|    |------------|--------------|----------|

params: "admin"、2、 "2020-04-20"、2、 "2020-04-20" allow on row B is not lower than 2020-04-20

  • 何も起こりません。

パラメータ:「admin」、2、「2020-04-22」、2、「2020-04-22」allow on row B2020-04-20よりも小さい:

  • upvotes on 行A増加(2-> 3
  • allow on row Bが更新されます(2020-04-20-> 2020-04-22

params: "bar"、1、 "2020-04-19"、1、 "2020-04-19" allow on row A is lower than 2020-04-19

  • upvotes on row Cが増加しました(1-> 2
  • allow on row Aが更新されます(2020-04-18-> 2020-04-19


更新2:

私がやりたいこと:ser 1が賛成したい場合ser 2(誰でも1日に1回しか他の人を反対票を投じることができます)、ボタンをクリックすると、allowed(on彼の行)は翌日と比較されます:

  • allowed翌日と等しい場合、それはser 1がすでに誰かに賛成していることを意味します(彼はser 2ユーザーまたは他の誰か)なので、何も変更されません
  • allowed翌日より小さい場合、それはser 1が誰かを賛成することを許可されていることを意味します。したがって、upvote on ser 2の行はser 1の行のallowがインクリメントされ、翌日に更新されます

「もしser 1またはser 2が実際に存在しない場合はどうなりますか」、または「もしser 1が試みたら自分自身を賛成しますか?* [〜#〜] php [〜#〜]コードで確認します。

3
Foxel

物事を見ていきましょう。まず、各列を修飾して、どの列がどのテーブルにあるかを確認します。

UPDATE a JOIN d ON a.`id` = d.`id`
    SET d.`upvotes`= d.`upvotes` + (a.`user`= ?),
        d.`allow` = (CASE WHEN a.`id` = ?
                   THEN ?
                   ELSE d.`allow`
                   END)
WHERE d.`id` = ? AND d.`allow` < ?;

ああ、2つのテーブルはidで1:1です。では、同じテーブルであるとしましょう。 (そして、おそらくそうすべきです。通常、1:1は良いスキーマ設計ではありません。)

UPDATE ad
    SET `upvotes`= `upvotes` + (`user`= ?),
        `allow` = (CASE WHEN `id` = ?
                   THEN ?
                   ELSE `allow`
                   END)
WHERE `id` = ? AND `allow` < ?;

CASEが無駄になっていることは明らかです。したがって、クエリは次のように簡素化されます。

UPDATE ad
    SET `upvotes`= `upvotes` + (`user`= ?),
        `allow` = ?
WHERE `id` = ? AND `allow` < ?;

では、2つのテーブルに戻りましょう。

UPDATE a JOIN d ON a.`id` = d.`id`
    SET d.upvotes = d.upvotes + (a.user = ?),
        d.allow = ?
    WHERE d.id = ? AND d.allow < ?;

それは別の形につながります。 (これが良いかどうかはわかりません。)

UPDATE d
    SET d.upvotes = d.upvotes +
                    ( ( SELECT  user = ? FROM a WHERE a.id = d.id ) )
        d.allow = ?
    WHERE d.id = ? AND d.allow < ?;

または、(ここでもおそらく大きな違いはありません):

UPDATE d
    SET d.upvotes = d.upvotes +
                EXISTS ( SELECT  user = ? FROM a WHERE a.id = d.id )
        d.allow = ?
    WHERE d.id = ? AND d.allow < ?;
1
Rick James

私はあなたの問題の解決策を見つけようとしました、そしておそらく私はここで正しいワークフローを理解している答えのIDを持っています:各ユーザーは1日に1回賛成投票できます

  1. user1がuser2に賛成
  2. 今日はすでにuser1に賛成票がありますか?
    ->はい:何もしません
    -> no:user1の日付を変更し、+ 1をuser2の賛成票に変更します

その例では、「admin」、2、「2020-04-20」、2、「2020-04-20」

  • 2 => user1
  • admin => user2
  • "2020-04-20" =>投票日

私は正しいですか?はいの場合、ここに私の解決策

mariadb

UPDATE data SET upvotes = CASE WHEN id = (SELECT id FROM accountlist WHERE accountlist.user = ?)
                AND 
               (SELECT allow FROM data inner join accountlist on accountlist.id = data.id 
                 where accountlist.id = ? )< ?
            THEN upvotes + 1
            ELSE upvotes
          END,
allow = CASE WHEN id = ?
THEN ?
ELSE allow
END

mysql

UPDATE data
SET upvotes = CASE WHEN id = (SELECT id FROM (select * from accountlist) as al WHERE al.user = "bar")
                    AND 
                   (SELECT allow FROM (select * from data) as d inner join (select * from accountlist) as al1 on al1.id = d.id where al1.id = 1)<"2020-04-19"
                THEN upvotes + 1
                ELSE upvotes
              END,
allow = CASE WHEN id = 1
THEN "2020-04-19"
ELSE allow
END;

作成されたデータベース:

         accountlist                       data
   |------------|---------------|    |------------|--------------|----------|
   | id         | user          |    | id         | allow        | upvotes  |
   |------------|---------------|    |------------|--------------|----------|
A: | 1          | admin         |    | 1          | 2020-04-18   | 2        |
B: | 2          | foo           |    | 2          | 2020-04-20   | 0        |
C: | 3          | bar           |    | 3          | 2020-04-22   | 1        |
   |------------|---------------|    |------------|--------------|----------|

値:「admin」、「2」、「2020-04-20」、「2」、「2020-04-20」=>変更なし

「admin」、「2」、「2020-04-22」、「2」、「2020-04-22」

         accountlist                       data
   |------------|---------------|    |------------|--------------|----------|
   | id         | user          |    | id         | allow        | upvotes  |
   |------------|---------------|    |------------|--------------|----------|
A: | 1          | admin         |    | 1          | 2020-04-18   | 3        |
B: | 2          | foo           |    | 2          | 2020-04-22   | 0        |
C: | 3          | bar           |    | 3          | 2020-04-22   | 1        |
   |------------|---------------|    |------------|--------------|----------|

「バー」、1、「2020-04-19」、1、「2020-04-19」=>

             accountlist                       data
   |------------|---------------|    |------------|--------------|----------|
   | id         | user          |    | id         | allow        | upvotes  |
   |------------|---------------|    |------------|--------------|----------|
A: | 1          | admin         |    | 1          | 2020-04-19   | 3        |
B: | 2          | foo           |    | 2          | 2020-04-22   | 0        |
C: | 3          | bar           |    | 3          | 2020-04-22   | 2        |
   |------------|---------------|    |------------|--------------|----------|

通常は正しく実行されます(オンラインでテスト済み)が、1つではなく2つのステートメントを使用するほうが簡単です。

1
Gautier Logeot