0

わかりました、これに a を使用したいのCASE STATEMENTですが、これで迷っています。基本的に、大量の行を更新する必要がありますが、「位置」列だけです。id_layout および id_layout_position ごとUPDATEに削除された位置の値よりも大きいすべての位置の値が必要です。position - 1

OK、これはテーブルがどのように見えるかの写真です: 代替テキスト http://acs.graphicsmayhem.com/images/dp_positions_table.png

ここで、丸で囲まれた行を削除するとします。これにより、位置 = 2 が削除され、0、1、3、5、6、7、および 4 が得られます。2 より大きい位置の値を再配置する必要があります。このように: 0、1、2、4、5、6、および 3。

OK、これが ROW の DELETING 内でこれまでに取得したものです。したがって、これをどのように探すかを次に示します (注: $module_info['clones'] = 削除するクローンの配列。上の表には何もないため、すべての id_clone 値は 0 であり、$module_info['id'] は削除する id_module 値です):

// Get rid of id_clone = 0, because we can't use them.
$module_info['clones'] = array_values(array_filter($module_info['clones']));

// Selecting the positions.
if (isset($module_info['clones'][0]))
    $query = 'id_module = {int:id_module} || id_clone IN ({array_int:id_clones})';
else
    $query = 'id_module = {int:id_module}';

$request = $smcFunc['db_query']('', '
    SELECT
        id_position, id_layout_position, id_layout, position
    FROM {db_prefix}dp_module_positions
    WHERE ' . $query,
    array(
        'zero' => 0,
        'id_module' => $module_info['id'],
        'id_clones' => $module_info['clones'],          
    )
);

while ($row = $smcFunc['db_fetch_assoc']($request))
{
    $module_info['position'][$row['id_layout']]['pos' . $row['id_position'] . $row['position'] . '_' . $row['id_layout_position']] = $row['position'];
    $module_info['id_positions'][] = $row['id_position'];
}

$smcFunc['db_free_result']($request);

// Remove all module and clone positions from the layout!
$smcFunc['db_query']('', '
    DELETE FROM {db_prefix}dp_module_positions
    WHERE id_position IN ({array_int:id_positions})',
    array(
        'id_positions' => $module_info['id_positions'],
    )
);

foreach($module_info['position'] as $id_layout => $id_layout_pos)
{
    foreach($id_layout_pos as $key => $position_val)
    {
        $lPos = explode('_', $key);
        $lPosId = (int) $lPos[1];

        $smcFunc['db_query']('', '
            UPDATE {db_prefix}dp_module_positions
            SET
                position = position - 1
            WHERE position > {int:position} AND id_layout = {int:id_layout} AND id_layout_position = {int:id_layout_position}',
            array(
                'id_layout' => (int) $id_layout,
                'position' => (int) $position_val,
                'id_layout_position' => $lPosId,
            )
        );
    }
}

NEW QUESTION: THERE's Just gotta be some sort of way to use a CASE STATEMENT in this UPDATE now. For Example, I now need to update all positions to position - 1, on a per id_layout, and per id_layout_position basis. BUT only where the position is greater than the current position for that id_layout and id_layout_position value.

Is there anyway to do this without using a FOREACH LOOP?

4

2 に答える 2

2

行を削除する前に、その位置を取得し、より高い位置にあるすべての行の位置をデクリメントすることができます。

擬似コード:

function deleteRow($id){
  DB::startTransaction()
  try{
    $infos = DB::getData('SELECT position FROM db_positions WHERE id_position = :ID', array(':ID' => $id));
    if(empty($infos)){
      throw("useless ID");
    }
    DB::query('DELETE FROM db_positions WHERE id_position = :ID', array(':ID' => $id));
    DB::query('UPDATE db_positions SET position = position - 1 WHERE position > :position', array(':position' => $infos['position']);
    DB::commit();
  }
  catch(Exception $e){
    DB::rollBack();
  }
}
于 2010-05-05T12:44:56.923 に答える
1

中断のないシーケンスで更新するには、次を使用できます。

SET @reset := 0; 
UPDATE dp_positions
SET
  positions = 
  CASE          
    WHEN id_layout_position > @reset THEN 
      IF(@pos:=0,NULL, @reset:=id_layout_position)        
    ELSE @pos := @pos + 1    
  END - 1  
ORDER BY id_layout_position, position;

注: @reset は、id_layout_position の変更時にカウンターを再起動すると仮定して、id_layout_position の最小値に設定する必要があります。より複雑な場合は、WHEN 条件を変更する必要があり、前の行の値を保持するためにより多くの変数が必要になる場合があります。
さらに、IF はハックです。@pos を 0 に設定し、他の開始値に設定しない限り、NULL になることはありません。mysql に 2 つの式を強制的に評価させる方法がわかりませんでした (誰か、エレガントなものはありますか?そこの?)

上記はテスト済みですが、列/テーブル名が異なります (再入力エラーの可能性があります)。

編集:私のテストはあまり良くありませんでした。上記にはエラーがあり、OPはフレームワークのセキュリティをバイパスする必要があるようです。修正とストアドプロシージャのバージョンは次のとおりです

phpmyadminまたは mysql コマンド ライン実行を使用する

delimiter //
CREATE PROCEDURE `renumerate`()
BEGIN
SET @pos := -1;
SET @reset := (SELECT MIN(id_layout_position) FROM dp_position);
UPDATE
  dp_positions
SET
  position = 
    CASE          
      WHEN b > @reset THEN 
        IF(@reset:=id_layout_position, @pos:=0, @pos:=0)        
      ELSE @pos := @pos + 1    
    END
ORDER BY id_layout_position, position;
END //
delimiter ;

(phpMyAdmin を使用する場合は、区切りステートメントを使用せず、インターフェースで直接区切り文字を // として指定します)。プロシージャを呼び出すと、通常の SQL が実行されます

$smcFunc['db_query']('', 'CALL renumerate()');

今回のテストがより良いものであることを願っています。

于 2010-05-05T12:14:42.760 に答える