好的,我想用CASE STATEMENT
来做这件事,但我迷失了。基本上,我需要更新大量的行,但只需更新“位置”列。我需要UPDATE
所有位置值都比每个id_layout和id_layout_position基础上移除到position - 1
的位置值更好。
好的,这是表格的图片: alt text http://acs.graphicsmayhem.com/images/dp_positions_table.png
现在让我说删除带圆圈的行,这将删除position = 2并给我:0,1,3,5,6,7和4.它应该重新定位大于2的位置值,所以它看起来像这样:0,1,2,4,5,6和3。
好的,这是我在ROW的DELETING中得到的内容,所以这里是它的查找方式(注意:$ module_info ['clones'] =要删除的克隆数组,在上表中会有none,因为所有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 STATEMENT即可。例如,我现在需要将所有位置更新到位置-1,每个id_layout和每个id_layout_position。但仅限于位置大于该id_layout和id_layout_position值的当前位置。
无论如何不使用FOREACH LOOP
吗?
答案 0 :(得分: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();
}
}
答案 1 :(得分: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是一个hack,只要你将@pos设置为0而不是其他一些起始值就永远不会为NULL,我只是不知道如何强制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()');
希望这次我的测试更好。