1

これが機能するSQLステートメント(実際には2つのステートメント)です。一連の一致する行を取得し、行delivery_numberごとに増分するを追加します。

SELECT @i:=0;
UPDATE pipeline_deliveries AS d
SET d.delivery_number = @i:=@i+1
WHERE d.pipelineID = 11
ORDER BY d.setup_time;

しかし今では、クライアントはそれらを。で注文することを望んでいませんsetup_time。別のテーブルのフィールドである出発時刻に従って注文する必要がありました。これを行う方法がわかりません。

MySQLのドキュメントとこの回答は、バージョン4.0以降(MySQL 5.0を実行している)ではこれを実行できるはずであることを示唆しています。

SELECT @i:=0;
UPDATE pipeline_deliveries AS d RIGHT JOIN pipeline_routesXdeliveryID AS rXd
    ON d.pipeline_deliveryID = rXd.pipeline_deliveryID
LEFT JOIN pipeline_routes AS r
    ON rXd.pipeline_routeID = r.pipeline_routeID
SET d.delivery_number = @i:=@i+1
WHERE d.pipelineID = 11
ORDER BY r.departure_time,d.pipeline_deliveryID;

しかし、エラーが発生します#1221 - Incorrect usage of UPDATE and ORDER BY

それで、正しい使用法は何ですか?

4

3 に答える 3

2

UPDATE2つ(またはそれ以上)のテーブルとを結合することはできませんORDER BY

次のような方法で、制限を回避できます。

UPDATE 
    pipeline_deliveries AS upd
  JOIN
    ( SELECT t.pipeline_deliveryID, 
             @i := @i+1 AS row_number 
      FROM 
          ( SELECT @i:=0 ) AS dummy
        CROSS JOIN 
          ( SELECT d.pipeline_deliveryID
            FROM 
                pipeline_deliveries AS d 
              JOIN 
                pipeline_routesXdeliveryID AS rXd
                  ON d.pipeline_deliveryID = rXd.pipeline_deliveryID
              LEFT JOIN 
                pipeline_routes AS r
                  ON rXd.pipeline_routeID = r.pipeline_routeID
            WHERE 
                d.pipelineID = 11
            ORDER BY 
                r.departure_time, d.pipeline_deliveryID
          ) AS t
    ) AS tmp
      ON tmp.pipeline_deliveryID = upd.pipeline_deliveryID
SET 
    upd.delivery_number = tmp.row_number ;

上記は、MySQLの2つの機能、ユーザー定義変数と派生テーブル内の順序付けを使用しています。後者は標準SQLではないため、MySQLの機能リリースでうまく機能しない可能性があります(オプティマイザーが、LIMIT句がない限り、派生テーブル内の順序付けが役に立たないことを理解するのに十分賢い場合)。実際、クエリは最新バージョンのMariaDB(5.3および5.5)でそれを正確に実行します。それはあたかもそこになかったかのように実行されORDER BY、結果は期待されませんでした。MariaDBサイトで関連する質問を参照してください:GROUPBYトリックが最適化されました

同じことが、オプティマイザコードを改善するメインストリームMySQLの将来のリリース(おそらく5.6で、誰かがこれをテストしたいと思っていますか?)でも非常によく起こる可能性があります。

したがって、これは標準SQLで作成することをお勧めします。最適なのは、まだ実装されていないウィンドウ関数です。ただし、更新の影響を受ける行の小さなサブセットを処理している限り、自己結合を使用することもできます。これは、効率に関してそれほど悪くはありません。

UPDATE 
    pipeline_deliveries AS upd
  JOIN
    ( SELECT t1.pipeline_deliveryID
           , COUNT(*) AS row_number
      FROM
          ( SELECT d.pipeline_deliveryID
                 , r.departure_time
            FROM 
                pipeline_deliveries AS d 
              JOIN 
                pipeline_routesXdeliveryID AS rXd
                  ON d.pipeline_deliveryID = rXd.pipeline_deliveryID
              LEFT JOIN 
                pipeline_routes AS r
                  ON rXd.pipeline_routeID = r.pipeline_routeID
            WHERE 
                d.pipelineID = 11
          ) AS t1
        JOIN
          ( SELECT d.pipeline_deliveryID
                 , r.departure_time
            FROM 
                pipeline_deliveries AS d 
              JOIN 
                pipeline_routesXdeliveryID AS rXd
                  ON d.pipeline_deliveryID = rXd.pipeline_deliveryID
              LEFT JOIN 
                pipeline_routes AS r
                  ON rXd.pipeline_routeID = r.pipeline_routeID
            WHERE 
                d.pipelineID = 11
          ) AS t2
          ON t2.departure_time < t2.departure_time
          OR t2.departure_time = t2.departure_time 
             AND t2.pipeline_deliveryID <= t1.pipeline_deliveryID
          OR t1.departure_time IS NULL
             AND ( t2.departure_time IS NOT NULL
                OR t2.departure_time IS NULL
                   AND t2.pipeline_deliveryID <= t1.pipeline_deliveryID
                 )
      GROUP BY
          t1.pipeline_deliveryID  
    ) AS tmp
      ON tmp.pipeline_deliveryID = upd.pipeline_deliveryID
SET 
    upd.delivery_number = tmp.row_number ;
于 2013-01-14T18:47:31.357 に答える
1

このドキュメントに基づく

複数テーブル構文の場合、UPDATEは、条件を満たすtable_referencesで指定された各テーブルの行を更新します。この場合、ORDERBYとLIMITは使用できません。

MySQLについてあまり知らなくても、カーソルを開いてこの行を1行ずつ処理するか、この処理を処理するために維持しているクライアントコード(PHP、Javaなど)にカーソルを戻すことができます。

さらに掘った後:

最適化が不十分なサブクエリを削除するには、サブクエリを結合として書き直す必要がありますが、それを実行してLIMITとORDERBYを保持するにはどうすればよいでしょうか。1つの方法は、FROM句のサブクエリで更新する行を見つけることです。これにより、LIMITとORDERBYをサブクエリ内にネストできます。このようにして、work_to_doは、それ自体の最も優先度の高い10行の未請求行に対して結合されます。通常、マルチテーブルUPDATEで更新ターゲットを自己結合することはできませんが、FROM句のサブクエリ内にあるため、この場合は機能します。

update work_to_do as target
   inner join (
      select w. client, work_unit
      from work_to_do as w
         inner join eligible_client as e on e.client = w.client
      where processor = 0
      order by priority desc
      limit 10
   ) as source on source.client = target.client
      and source.work_unit = target.work_unit
   set processor = @process_id;

欠点が1つあります。それは、行が主キーの順序でロックされていないことです。これは、このテーブルで時折発生するデッドロックを説明するのに役立つ場合があります

于 2013-01-14T18:32:36.917 に答える
0

難しい方法:-

    ALTER TABLE eav_attribute_option
        temp_value TEXTNOTNULLを追加します
        sort_orderの後;
    UPDATE eav_attribute_option o
        JOIN eav_attribute_option_value ov ON o.option_id = ov.option_id
        SET o.temp_value = ov.value
        WHERE o.attribute_id = 90;
    SET @x = 0;
    UPDATE eav_attribute_option
        SET sort_order =(@x:= @ x + 1)
        WHERE attribute_id = 90
        order BY temp_value ASC;
    ALTER TABLE eav_attribute_option
        DROP temp_value;

于 2017-12-10T23:15:59.930 に答える