2010-05-05 107 views
0

好吧,我想用这个CASE STATEMENT,但我迷失了这个。基本上,我需要更新大量的行,但只是在“职位”列。我需要UPDATE所有的位置值都大于位置值,该位置值以id_layout和id_layout_position为基础被移除到position - 1SQL:使用CASE语句一次更新1000行

OK,这里是什么样的表看起来像一个PIC: alt text http://acs.graphicsmayhem.com/images/dp_positions_table.png

现在让我们假设我删除盘旋行,这将删除位置= 2,给我:0,1,3,5,它应该重新定位大于2的位置值,以便它看起来像这样:0,1,2,4,5,6和3.

好的,这是我的(注意:$ 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: 这里还有得是某种方式在这个UPDATE现在使用CASE语句。例如,我现在需要将所有职位更新到位置-1,每个id_layout和每个id_layout_position基础。但只有位置大于id_layout和id_layout_position值的当前位置的位置。

有没有办法做到这一点,而不使用FOREACH LOOP

+1

你的问题是很难理解...... – 2010-05-05 10:54:46

+0

好了,问题是,我该如何使用在第一个代码示例一个'CASE statement',这样我可以更新所有[0] - 找到的总行数是否在'position'列中的行中,具有'id_layout'值和'id_layout_position'值,并且该表中的所有不同'id_layout'值都会继续执行此操作? – SoLoGHoST 2010-05-05 10:58:53

+1

你仍然在寻求你的解决方案:说明你实际上想要达到的目标,而不是你认为你应该怎么做? – 2010-05-05 11:01:35

回答

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(); 
    } 
} 
+0

现在这是一个想法...嗯,谢谢:) – SoLoGHoST 2010-05-05 12:58:34

+0

好的,这个代码有问题,你删除了id位置,而更新是从位置列中获取所有信息而不是信息从不存在的id_position值...嗯 – SoLoGHoST 2010-05-05 13:38:28

+0

而比如果你尝试更新它,你会遇到另一个问题.... OMG,如何做到这一点... – SoLoGHoST 2010-05-05 13:40:17

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而不是其他初始值,我就不知道如何强制mysql来评估两个表达式(任何人,是那里有什么优雅的东西?)

上面已经过测试,但有不同的列/表名(重新输入错误可能)。

编辑:我的测试是不太好,上面有错误,而且也似乎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()'); 

希望我的测试是更好的这段时间。

+0

你好,我不确定SMF会允许我在查询中执行':=',并且我不能在不使用$ smcFunc类的情况下访问数据库,请看看SMF如何在我的问题的第二个代码块中进行操作,也许您可以将相同的风格纳入您的回答?我真的很感激它。谢谢:) – SoLoGHoST 2010-05-05 12:20:49

+0

另外,在您的查询中,我没有看到您设置位置列值的位置? 'dp_positions'应该是'position'列吗?但是表名又怎么样呢?我看到你里面有'two',而不是表名... – SoLoGHoST 2010-05-05 12:28:03

+0

嗯,检查SMF的深度并不是真的很痒。尝试将上面的SQL作为2条语句运行,它可能对过敏性更强;然后到:= – Unreason 2010-05-05 12:28:56