SQL Server: если запись найдена, обновите ее, иначе вставьте, исправление ошибок запроса на слияние

Я придумал следующий запрос для SQL Server для обновления строки, если найдено еще вставить

merge tblpermissions  as t
using (select cid, uid from tblpermissions) as s
on (t.cid = s.cid and t.uid=s.uid)
when not matched
THEN INSERT (cid, uid, [read], [write], [readonly], [modify], [admin]) VALUES ('1',   '1', 1, 1, 0, 1, 1) 
when matched 
THEN UPDATE SET [read]=1, write=1, readonly=0, modify=1, admin=1 ;

Хотя он не выдает никаких ошибок, он не достигает того, что я ожидаю. в таблице нет записи и не вставляется новая запись.

Любая коррекция?

Изменить: учитывая ваши предложения, я изменил его, как показано ниже, но без ожидаемого результата -

MERGE INTO tblpermissions  as t
using (SELECT '1' AS cid, '1' AS uid FROM tblpermissions) as s
on (t.cid = s.cid and t.uid=s.uid)
WHEN NOT MATCHED 
THEN INSERT (cid, uid, [read], [write], [readonly], [modify], [admin])
VALUES  ('1', '1', 1, 1, 0, 1, 1) 
WHEN MATCHED
THEN UPDATE SET [read]=1, write=1, readonly=0, modify=1, admin=1 ;

Редактировать: пожалуйста, проверьте комментарий внизу, ниже приведен улучшенный запрос по вашим предложениям, не говоря уже о том, что он не будет работать должным образом, поскольку снова принимает ту же таблицу.

MERGE INTO tblpermissions  as t
using tblpermissions as s
on (t.cid = s.cid and t.uid=s.uid)
WHEN NOT MATCHED 
THEN INSERT (cid, uid, [read], [write], [readonly], [modify], [admin])
VALUES  ('1', '1', 1, 1, 0, 0, 1) 
WHEN MATCHED
THEN UPDATE SET [read]=1, write=1, readonly=0, modify=1, admin=1 ;

Редактировать: выберите-если найдено обновление-иначе вставьте альтернативу ниже -

$query = "SELECT * FROM ".SQL_PREFIX."permissions\n"
              ."WHERE cid='".$cid."' AND uid='".$uid."'";
    $sth = $this->dbh->query($query);
    $res = $sth->fetch();
    //print_r($res);var_dump($res);
    if(!$res || $res==null)
    {
        $query = "INSERT INTO ".SQL_PREFIX."permissions (cid, uid, [read], [write], [readonly], [modify], [admin])\n"
            ."VALUES ('$cid', '$uid', ".implode(", ", $values).")";
        if(!($sth = $this->dbh->query($query)))
            $this->db_error(__('Error inserting user permissions.'),
                $query);
    }else{
        $query = "UPDATE ".SQL_PREFIX."permissions SET ".implode(", ", $sets).";";
        if(!($sth = $this->dbh->query($query)))
            $this->db_error(__('Error updating user permissions.'),
                $query);
    } 

Изменить: Ниже приведено решение mysql для этой ситуации:

ВСТАВЬТЕ в tblpermissions (cid, uid, [чтение], [запись], [только чтение], [изменение], [админ]) ЗНАЧЕНИЯ ('1', '1', 1, 1, 0, 0, 1) НА ДУБЛИКАЦИИ ОБНОВЛЕНИЕ КЛЮЧА [чтение] = 1, запись = 1, только чтение = 0, изменение = 1, администратор = 1

который просто использует ключевое слово DUPLICATE KEY. Также хотелось бы еще раз подчеркнуть, что существует уникальное ключевое ограничение на комбинацию cid и uid. Итак, что делает mysql, так это то, что сначала он просто ищет комбинацию cid и uid, которую мы пытаемся вставить, и, если найдены обновления, он вставляет новую запись.


person code-match    schedule 09.05.2013    source источник
comment
Вы сливаетесь в tblPermissions, выбирая из того же tblPermissions?!?!? Я предполагаю, что все строки, выбранные в s, будут уже внутри tblPermissions (поскольку вы выбрали их оттуда) - так что вы, очевидно, когда-либо получите только часть when matched . ...   -  person marc_s    schedule 09.05.2013


Ответы (1)


Поскольку исходная и целевая таблицы одинаковы, никогда не бывает несовпадающих строк. Все строки в tblpermissions обновляются.

Для вставки исходная таблица (определенная с помощью using) должна отличаться от целевой таблицы, как в следующих примерах:

merge tblpermissions  as t
using (select cid, uid from otherTable) as s
on (t.cid = s.cid and t.uid = s.uid)

merge tblpermissions  as t
using (select cid, uid from tblpermissions) as s
on (t.cid = s.cid + 1 and t.uid = s.uid + 1)

Изменить: поскольку вы используете постоянные значения вместо результата выбора из любой таблицы, возможно, следующее поможет:

merge tblpermissions as t
using (select '1' cid, '1' uid, 1 [read], 1 [write], 0 [readonly], 0 [modify], 1 [admin]) s
on (t.cid = s.cid and t.uid = s.uid)
when matched 
    then update set [read] = s.[read], write = s.[write], readonly = s.[readonly], modify = s.[modify], admin = s.[admin]
when not matched
    then insert (cid, uid, [read], [write], [readonly], [modify], [admin]) values (s.cid, s.uid, s.[read], s.[write], s.[readonly], s.[modify], s.[admin]);

Хотя я думаю, что вставлять только одно слияние строк - это излишество, было бы проще сделать следующее:

if exists (select 1 from tblpermissions where cid = '1' and uid = '1')
update tblpermissions set [read] = 1, write = 1, readonly = 0, modify = 1, admin = 1 where cid = '1' and uid = '1'
else
    insert tblpermissions (cid, uid, [read], [write], [readonly], [modify], [admin]) VALUES ('1', '1', 1, 1, 0, 1, 1)

Я не думаю, что у SQL Server есть более короткий или элегантный способ выполнить это, ничего похожего на on duplicate из MySQL.

person dang    schedule 09.05.2013
comment
хорошо, я понимаю вашу точку зрения, но я просто хочу добиться этого - если запись с определенным идентификатором существует, обновите ее, иначе вставьте новую запись. В этом случае существует комбинация cid и uid, затем обновите разрешения для них, иначе вставьте новую запись разрешений. - person code-match; 09.05.2013
comment
Проблема в том, что вы сравниваете одни и те же комбинации cid и uid сами с собой. Например, если tblpermissions содержит строку с cid = 1 и uid = 1, то она будет сравниваться с той же строкой с cid = 1 и uid = 1. , и то же самое касается каждой строки в таблице. В конце концов, не может быть определенного идентификатора, которого не существует, потому что вы сравниваете одни и те же идентификаторы сами с собой. - person dang; 09.05.2013
comment
хорошо, так это то, что «использование (некоторый запрос) как s» определяет источник для сравнения с целевой таблицей. - person code-match; 10.05.2013
comment
@jhackspmarrow Точно. На самом деле это не обязательно должен быть запрос, вы можете напрямую использовать таблицу, например using otherTable on a = b. - person dang; 10.05.2013
comment
@jhackspmarrow Вы нашли ответ на этот вопрос? Если это то, что я предложил, не могли бы вы отметить это как правильное? Или дайте мне знать, как можно улучшить мой ответ, или, может быть, даже опубликуйте свой собственный ответ и отметьте его как правильный. - person dang; 13.05.2013
comment
all n @dang извините за задержку ответа. Веб-сайт ms объясняет синтаксис для слияния по адресу technet.microsoft.com/en-us/library/ Я понял, а также из ваших комментариев, что он ожидает "другую таблицу" в предложении using, но, к сожалению, это не так. то, что я ищу. Мне нужно выполнить эту операцию на «той же таблице». Пока не удалось придумать лучшего решения, кроме традиционного способа выбора записи сначала - если найдено, обновить, а затем вставить. - person code-match; 15.05.2013
comment
@jhackspmarrow Вы можете прекрасно объединить одну и ту же таблицу саму по себе, вам просто нужно установить условие слияния на что-то, что позволяет сопоставлять разные строки. Если cid и uid являются PK для этой таблицы, у вас не может быть разных наборов в источнике и цели. Можете ли вы опубликовать свой код, чтобы сначала выбрать записи, а затем решить, что делать? Может быть, мы сможем помочь превратить это в слияние. - person dang; 15.05.2013
comment
Еще раз извините @dang за поздний ответ, отредактировал вопрос, пожалуйста, найдите альтернативу select-n-update выше. - person code-match; 20.05.2013
comment
Похоже, что значения для cid и uid берутся не из таблицы, а из некоторых переменных PHP. Если это так, я полагаю, это нельзя превратить в слияние, поскольку для сравнения требуются данные, которые отсутствуют исключительно из запросов SQL. - person dang; 20.05.2013
comment
хорошо, но я буду продолжать думать об этом. У mysql есть свой способ справиться с такой ситуацией, интересно, почему mssql не может его предоставить. - person code-match; 22.05.2013
comment
Какое решение MySQL для этого? Возможно, мы сможем придумать что-то подобное в SQL Server. - person dang; 22.05.2013
comment
Хорошо @Dang, спасибо, что все еще проявляете интерес. включил решение MySQL выше и пытался найти эквивалент MS SQL для него. - person code-match; 24.05.2013