Как выбрать SCOPE_IDENTITY() с помощью драйвера Microsoft SQL в PHP?

У меня есть специальный оператор MSSQL, который я использую для получения идентификатора указанной строки, которая либо уже существует, либо вставляет INSERT, а затем извлекает этот идентификатор. Этот SQL отлично работает в SQL Management Studio, а также в паре моих приложений .NET C#.

К сожалению, меня попросили использовать PHP для этой конкретной задачи, и я испытываю большие трудности при попытке использовать PHP и официальный драйвер Microsoft SQL.

Оператор не возвращает идентификатор после INSERT (или, если это так, я не могу его получить). Может кто-нибудь посмотреть, где и если я ошибаюсь?

Я использую PHP 5.3.23 с драйвером Microsoft SQL 3.0. Вот мой код:

$sql = ''
        . 'IF (SELECT COUNT(*) FROM users WHERE sAMAccountName = ?) = 0 '
        . 'BEGIN '
        . 'INSERT INTO users (role, sAMAccountName, fullName) '
        . 'VALUES (1, ?, ?) '
        . 'SELECT SCOPE_IDENTITY() '
        . 'END '
        . 'ELSE SELECT id FROM users WHERE sAMAccountName = ?';
$params = array($requester, $requester, $fullName, $requester);
$query = $this->SqlQuery($sql, $params);

function SqlQuery($sql, array $params) {
    $conn = $this->SqlConnection(null, null, null, null); // x4 null = use default connection options
    $query = sqlsrv_query($conn, $sql, $params);
    if ($query) {
        sqlsrv_fetch($query);
        echo 'Result: ' . sqlsrv_get_field($query, 0); // This should return an ID regardless
        sqlsrv_close($conn);
    }
}

Вот результат HTML (без результата):

Result: 

person Robula    schedule 14.07.2014    source источник
comment
Я не очень хорошо знаком с T-SQL, поэтому, пожалуйста, потерпите меня, если я спрашиваю чепуху, но... Это полный автономный код? Это все одна строка, без разделителя, например. ВСТАВИТЬ и ВЫБРАТЬ. Выглядит странно, что он вообще работает :-?   -  person Álvaro González    schedule 16.07.2014
comment
@ ÁlvaroG.Vicario Единственное, что в SQL зависит от строки, — это комментарий к строке (--). Помимо этого все в SQL (и T-SQL), включая несколько операторов, можно писать без разрывов строк. Традиционно разделители операторов (;) не были необходимы, хотя они необходимы для новых ключевых слов операторов (WITH, RECEIVE, ..) и потребуются в будущем.   -  person RBarryYoung    schedule 22.07.2014
comment
@RBarryYoung - Спасибо за указатель.   -  person Álvaro González    schedule 22.07.2014


Ответы (3)


Вам нужен SET NOCOUNT ON; в начале вашего запроса.

Ваш оператор INSERT на самом деле что-то возвращает: пустой набор записей плюс сообщение «(1 затронута строка)». SSMS просто объединяет все наборы записей, но в sqlsrv вам нужно явно перейти ко второму набору записей, чтобы увидеть результат SCOPE_IDENTITY(). При отключении функции подсчета строк неинтересный набор результатов удаляется, и он будет делать то, что вы хотите.

person Robert Calhoun    schedule 22.07.2014
comment
Полезно знать и объяснить, почему мое утверждение работает в SSMS и даже в приложении C #. Тем не менее, даже без SET NOCOUNT ON; я все равно должен увидеть результат SELECT SCOPE_IDENTITY(), если я перейду к следующему набору записей, используя sqlsrv_next_result($query); sqlsrv_fetch($query); $id = sqlsrv_get_field($query, 0);? Я почти уверен, что пробовал это, и все, что я получил, было ложным. - person Robula; 23.07.2014
comment
Итак, вы пробовали SET NOCOUNT ON, и это решило вашу проблему? - person Robert Calhoun; 23.07.2014

Не уверен, что это вариант для вас, но: я бы изменил оператор SQL на хранимую процедуру и включил возврат идентификатора. Как только хранимая процедура внесет эти изменения в оператор

'SELECT SCOPE_IDENTITY() '

to

'SELECT SCOPE_IDENTITY() as id'

Затем в PHP вы сможете получить доступ к идентификатору как к данным. Хранимые процедуры будут возвращать данные в виде данных, не уверен на стороне PHP, я человек .NET

Если вам нужна помощь с преобразованием инструкции в хранимую процедуру, дайте мне знать.

РЕДАКТИРОВАТЬ:
Предполагая, что sAMAccountName является первичным ключом в этой таблице, и что вы указываете в своем вопросе, что вы не можете вернуть личность только во время INSERT. Тогда изменение SQL на это поможет вам.

$sql = ''
    . 'IF (SELECT COUNT(*) FROM users WHERE sAMAccountName = ?) = 0 '
    . 'BEGIN '
    . 'INSERT INTO users (role, sAMAccountName, fullName) '
    . 'VALUES (1, ?, ?) '
    . 'END '
    . 'SELECT id FROM users WHERE sAMAccountName = ?';
person Dbloch    schedule 14.07.2014
comment
Хранимая процедура на самом деле не вариант для меня. Мне придется просто запустить совершенно отдельный SQL-запрос в другом соединении, если я не смогу заставить это работать. Спасибо - person Robula; 15.07.2014
comment
Мне кажется, что, поскольку вы делаете это в нескольких местах в разных приложениях, хранимая процедура - единственное, что действительно имеет смысл. Надеюсь, вам никогда не придется это менять, так как теперь у вас один и тот же код в нескольких приложениях. - person Sean Lange; 17.07.2014
comment
Изменил подход в моем ответе выше. - person Dbloch; 17.07.2014
comment
Спасибо за вашу помощь, я пробовал все вышеперечисленное и не могу получить идентификатор на вставке. Теперь я предполагаю, что драйвер Microsoft SQL для PHP неспособен, и мне пришлось использовать хранимую процедуру SQL. - person Robula; 18.07.2014

Вы могли бы попробовать это. Удалите лишние одинарные кавычки и конкатенации и примените предложение Дблоха.

$sql = 'IF (SELECT COUNT(*) FROM users WHERE sAMAccountName = ?) = 0
            BEGIN
            INSERT INTO users (role, sAMAccountName, fullName)
            VALUES (1, ?, ?) 
            SELECT SCOPE_IDENTITY() as id
            END
            ELSE SELECT id FROM users WHERE sAMAccountName = ?';
person VikingBlooded    schedule 16.07.2014
comment
+1 Трудно поверить, что SQL Server Management Studio может безукоризненно выполнять такую ​​однострочную работу. Я уверен, что в опубликованном коде на самом деле отсутствуют разделители, либо возврат каретки, либо точка с запятой. - person Álvaro González; 19.07.2014
comment
Должен быть, я сам тестировал его в SSMS, и он работал отлично. - person VikingBlooded; 20.07.2014