Updating a local variable in an UPDATE is known as the "quirky update". It is something that is typically used when needing to carry a value across rows of an update in scenarios such as a running total. This was more important of a feature prior to the windowing functions introduced in SQL Server 2012. There is a fairly good discussion of it here that has links to various other articles and forums that would be beneficial to read:
Please can somebody explain how quirky updates work?
In this particular case it does not appear that it provides any benefit over doing this in a SELECT. This is because there is no field of the table being updated at the same time as the variable is being set. Being able to deal with a real field in addition to a variable is not something that can be done in a SELECT statement (i.e. you cannot do SELECT field1, @variable = @variable + field2 FROM Table), which is why the quirky update is sometimes quite handy. Hence, this particular usage could be turned into a SELECT with no functional difference as it is just creating a Dynamic SQL string.
The only possible functional difference in this particular usage between an UPDATE and SELECT would potentially be the order of the values concatenated into the string. The reason is that the quirky update is ordered by the Clustered Index whereas changing this to a SELECT, with no additional changes to add an ORDER BY, will have no guaranteed order.
In terms of logging, since nothing is being updated in this UPDATE statement, nothing is written to the Transaction Log. I have confirmed this by testing the variable-only scenario (i.e. the current question) as well as an UPDATE that does have a field specified in the SET clause but no rows match the WHERE clause. In neither case was anything logged, not even if an explicit transaction was started and committed.
Please note that, with respect to locking, if you are using the SNAPSHOT transaction isolation level, then fewer (and less impacting) locks will be taken when using an UPDATE so there should be little difference between it and using a SELECT (since no rows are actually being updated here). Otherwise, both the UPDATE and SELECT appear to take the same number of locks. The difference, though, is that the UPDATE takes "Exclusive" locks whereas the SELECT only takes "Shared" locks. Since "Shared" locks are less impacting to other processes and allow for more concurrency, given that no actual field in this table is being updated, this query really should be converted to a SELECT:
SELECT @expr = ISNULL(@expr + ' union all ', '')
+'select '
+ cast(pozycja AS VARCHAR(50)) + ','
+ cast(schemat AS VARCHAR(50)) + ','
+ cast(nardolny AS VARCHAR(50)) + ','
+ cast(nargorny AS VARCHAR(50)) + '' +
CASE WHEN warunekSQL<>'' THEN ' where ' + warunekSQL
ELSE ''
END
FROM dbo.dbr_schem
WHERE indeks = @indeks;
Please note that in the query above, I have specified a length/size for the CAST AS VARCHAR which is otherwise unspecified in the query in the Question. Variable length datatypes should not be left as unspecified as the default is either 1 or 30 depending on the situation in which it is being done.
update. Usually this sort of processing is done usingselect.updateandselectin this case ?updatemight incur extra overhead for logging. It is probably slightly more expensive than aselect, because it has to lock rows for updates, even if nothing changes