为每个ID子集更新/重置列值(使用"计数器")

时间:2017-11-03 16:32:56

标签: mysql sql

所以,我有一个表格(+----------+-------+ | id | order | |----------+-------| | 6c1e1f12 | 4 | |----------+-------| | 6c1e1f12 | 7 | |----------+-------| | 6c1e1f12 | 2 | |----------+-------| | 6c1e1f12 | 3 | |----------+-------| | 6c1e1f12 | 6 | |----------+-------| | 5d9f1892 | 5 | |----------+-------| | 5d9f1892 | 1 | +----------+-------+ ),如下所示:

order

我要做的是:"重置"每个现有id记录组的+----------+-------+ | id | order | |----------+-------| | 6c1e1f12 | 0 | |----------+-------| | 6c1e1f12 | 1 | |----------+-------| | 6c1e1f12 | 2 | |----------+-------| | 6c1e1f12 | 3 | |----------+-------| | 6c1e1f12 | 4 | |----------+-------| | 5d9f1892 | 0 | |----------+-------| | 5d9f1892 | 1 | +----------+-------+ 值,从零(每次)开始。所以结果应该是:

12

我不介意实际的顺序 - 或者之前的行排序方式,例如,如果以前将order作为0的行变成了一行在此之后使用SELECT IF(@prev != a.id, @curr := 0, @curr := @curr + 1) AS order, @prev := a.id AS item_id FROM (SELECT id FROM order_items, (SELECT @curr := 0, @prev := '') b ORDER BY id) a; ...等等。

我有这个查询以我想要的方式返回值:

UPDATE

...但我构建的order查询只是将1值设置为UPDATE order_items, ( SELECT IF(@prev != a.id, @curr := 0, @curr := @curr + 1) AS order, @prev := a.id as item_id FROM (SELECT id FROM order_items, (SELECT @curr := 0, @prev := '') b ORDER BY id) a ) AS tmp SET order_items.order = tmp.order WHERE order_items.id = tmp.item_id; ,而不管:{/ p>

protected void writeTransaction()
{
    DateTime todaysDate = DateTime.Now;

    GridViewRow row = pickListGridview.SelectedRow;
    TextBox trQty = row.FindControl("TextBox1") as TextBox;
    string unitMeasure = row.Cells[6].Text;
    string itemNum = row.Cells[1].Text;
    string location = row.Cells[7].Text;
    string ordNum = orderNumTextBox.Text.ToUpper();

    OleDbCommand cmdWrite = new OleDbCommand("INSERT INTO TRDATA (ACREC, ACSEQ, ADJDT, ADJTM ,APCOD, APHMS, APRJC, BADGE, BLKSQ, CONO, CRWYN, CTLID, EFFIC, ENTUM, " +
        "EXFLG, FDATE, IPLOC, ITNBR, LBTIM, LCQC2, LINSQ, LLOCN, LNKNO, LPQC1, MATIM, MSCST, MSQTY, MUCST, MUQTY, ODUDT, ORDNO, PARNT, PLIST, PNREF, QCNTR, QPIEC, QUEUE," +
        " RJQTY, RLIST, RNREF, RSUPF, SCRAP, SDATE, SELYN, SEQNM, SHFTC, TCOST, TDATE, TRAMT, TRFMT, TRNA2, TRNN2, TRNC2, TRNNO, TRNNOO, TRQTY, TSTAT, TSTATS, TTIME, TURFA, " +
        "TURFN, TURFC, TURNA, TURNN, TURNC, USER2, VRQTY, TRFG, ESHR, WTSK) " +
        $"VALUES('Y', '0', '0', '0', 'I', '0', '0', '4153', '0', '0', 'N', '*', '0', '{unitMeasure}', '0', '1141208', '1', '{itemNum}', '0', '0', '0', '{location}', '0', '0', " +
        $"'0', '0', '0', '0', '0', '0', '{ordNum}', '0', '0', '0', '0', '0', '1', '0', '0', '0', '0', '0', '0', '0', '29', '1', '0', '1171103', '0', 'IP', '0'," +
        $" '0', '0', '8888889', '0', '{trQty.ToString()}', '2', '0', '85409', '0', '0', '0', '2', '8728517', '5', 'PPL000000017017', '0', '0', '0', '0')", cnTrData);

    cnTrData.Open();
    cmdWrite.ExecuteNonQuery();
    cnTrData.Close();
}

This是一个带有初始架构和一些数据的SQL小提琴。

1 个答案:

答案 0 :(得分:1)

MySQL参考手册警告用户定义变量的行为无法保证:

  

作为一般规则,除了在SET语句中,您不应该为用户变量赋值并在同一语句中读取值。 ...

     

您可能会得到您期望的结果,但这不能保证。 ...

     

涉及用户变量的表达式的评估顺序未定义。

https://dev.mysql.com/doc/refman/5.7/en/user-variables.html

注意到这一点,我们确实通过精心构造的SQL语句观察到一致的行为。

我使用了这种类型的模式,使用MySQL 5.6的用户定义变量成功。 (我希望在MySQL的未来版本中,对优化器进行改进后,这可能无法继续工作。

鉴于当前声明的目标,我倾向于这样写:

 UPDATE (
          SELECT @curr := IF(o.id <> @prev, 0, @curr + 1) AS order
               , @prev := o.id                            AS item_id
               , o.pk_col 
            FROM ( SELECT @curr := 0, @prev := '') i
           CROSS
            JOIN order_items o
           ORDER BY o.id
         ) s
    JOIN order_items t
      ON t.pk_col = s.pk_col
     SET t.order  = s.order

我没有测试过这个特定的声明,但这是我使用的模式。

一些建议:

避免使用联接操作的逗号语法,而是使用JOIN关键字。

确保ORDER BY适用于正在进行评估并分配给用户定义变量的SELECT

IF之外的udv进行分配(我希望首先评估IF函数中的表达式,然后进行赋值。< / p>

使用表名或表别名限定所有列引用。

作为样式首选项,我将别名s分配给源(派生表/内联视图),并将别名t分配给目标表(我正在更新的表) 。

我单独测试内联视图查询,以确保它返回我期望的结果。

如果此模式停止工作,如果我无法开始工作,那么我准备调整为使用中间临时表以确保在UPDATE之前实现视图查询。

DROP TEMPORARY TABLE IF EXISTS __tt_vq__ ;
CREATE TEMPORARY TABLE __tt_vq__ ( ... ) ;

使用用户定义的变量执行查询并实现

INSERT INTO __tt_vq__  SELECT s.* FROM ( view query ) s ;

然后验证结果是否符合我的预期(在我执行UPDATE之前完成对用户定义变量的操作)

UPDATE __tt_vq__ s
  JOIN target t 
    ON t.id = s.id
   SET t.col = s.val
;

DROP TEMPORARY TABLE __tt_vq__ ;

这种使用用户定义变量的方法特定于MySQL。其他数据库(例如SQL Server和Oracle)提供分析功能,例如ROW_NUMBER() OVERDENSE_RANK()。我们使用用户定义的变量来模拟该功能。

要做旧学校,没有用户定义的变量,没有分析函数,我们需要一个保证唯一的列(或一组列)。然后我们可以执行semijoin操作并获得“当前行”之前(小于)(之前)的行数。也就是说,我们想到“order”列表示“在此之前的行数”,因此第一行在它之前有0行,第二行在它之前有1行,等等。

*编辑*

随着SQLFiddle被添加到问题中,我们现在看到表order_items有一个item_id列和一个id列(实际上order列名为rank

SQL Fiddle demonstration here: http://sqlfiddle.com/#!9/9caf62/1

 -- #####################################
 -- added 2017-11-03 20:18:17 spencer7593

 UPDATE (
          SELECT @curr := IF(o.item_id <> @prev, 0, @curr + 1) AS rank
               , @prev := o.item_id                            AS item_id
               , o.id                                          AS id
            FROM ( SELECT @curr := 0, @prev := '') i
           CROSS
            JOIN order_items o
           ORDER BY o.item_id, o.id
         ) s
    JOIN order_items t
      ON t.id    = s.id
     SET t.rank  = s.rank