SELECT * INTO retains ORDER BY in SQL Server 2008 but not 2012 - sql

Execute the following SQL in 2008 and 2012. When executed in 2008, the returned result is in its correct sort order. In 2012, the sortorder is not retained.
Is this a known change? Is there a work-around for 2012 to retain the sort order?
CREATE TABLE #MyTable(Name VARCHAR(50), SortOrder INT)
INSERT INTO #MyTable SELECT 'b', 2 UNION ALL SELECT 'c', 3 UNION ALL SELECT 'a', 1 UNION ALL SELECT 'e', 5 UNION ALL SELECT 'd', 4
SELECT * INTO #Result FROM #MyTable ORDER BY SortOrder
SELECT * FROM #Result
DROP TABLE #MyTable
DROP TABLE #Result

How can you tell what the order is inside a table by using select * from #result? There is no guarantee as to the order in a select query.
However, the results are different on SQL Fiddle. If you want to guarantee that the results are the same, then add a primary key. Then the insertion order is guaranteed:
CREATE TABLE MyTable(Name VARCHAR(50), SortOrder INT)
INSERT INTO MyTable SELECT 'b', 2 UNION ALL SELECT 'c', 3 UNION ALL SELECT 'a', 1 UNION ALL SELECT 'e', 5 UNION ALL SELECT 'd', 4
select top 0 * into result from MyTable;
alter table Result add id int identity(1, 1) primary key;
insert into Result(name, sortorder)
SELECT * FROM MyTable
ORDER BY SortOrder;
I still abhor doing select * from Result after this. But yes, it does return them in the correct order in both SQL Server 2008 and 2012. Not only that, but because SQL Server guarantees that primary keys are inserted in the proper order, the records are even guaranteed to be in the correct order in this case.
BUT . . . just because the records are in a particular order on the pages doesn't mean they will be retrieved in that order with no order by clause.

When using ORDER BY with an INSERT, it has never been guaranteed to do anything other than control the order of the identity column if present.
Prior to SQL Server 2012, the optimizer always produced a plan as if an identity column existed and thus appears to order correctly. SQL Server 2012 correctly does not assume an identity column exists, and only orders if the table actually has an identity column.
So you can resolve this issue by adding an Identity column to your temp result table.
However, you really should just add an ORDER BY clause to your SELECT statement? SELECT statements without an ORDER BY have never been guaranteed to return the results in any specific order. Always add the ORDER BY clause to ensure you receive the results the way you expect.

First, thanks sgeddes for the explanation, it helped a lot.
The thing about defining a table variable or creating a temp table is you have to define it, and if you are going to go through the work of defining it, you might as well do the insert the correct way:
INSERT INTO #Result (col1, col2...)
SELECT Col1, Col2... FROM #MyTable....
In my case, the ORDER BY in the INSERT was dynamic so when I called "SELECT * FROM #Result", the ORDER BY was unknown. My solution was to add a ROW_NUMBER column that I could hardcode into the SELECT when I was getting the data.
Yea, I still have to include an ORDER BY, but at least it's static. Here's what I did:
--Insert
SELECT ROW_NUMBER() OVER (ORDER BY T.SortOrder ASC) AS RowNum, T.*
INTO #Result
FROM (SELECT * FROM #MyTable ...) AS T;
--Get data out
SELECT * FROM #Result ORDER BY RowNum;
Hope this helps.

Workaround :
You could add a SET ROWCOUNT before this type of query, then put if back to zero after to reset it, it works. This will force SQL to keep the order in your query.
SET ROWCOUNT 1000000000
CREATE TABLE #MyTable(Name VARCHAR(50), SortOrder INT)
INSERT INTO #MyTable SELECT 'b', 2 UNION ALL SELECT 'c', 3 UNION ALL SELECT 'a', 1 UNION ALL SELECT 'e', 5 UNION ALL SELECT 'd', 4
SELECT * INTO #Result FROM #MyTable ORDER BY SortOrder
SELECT * FROM #Result
SET ROWCOUNT 0
DROP TABLE #MyTable
DROP TABLE #Result

If you have different sorted results when querying each database, your collation is probably different between the two.
Try explicitly setting the collation in your query and see if your results are returned in the same order in both databases, e.g.
SELECT * FROM #Result ORDER BY C1 COLLATE Latin1_General_CS_AS

You must to create ROW_NUMBER() order by column you want to order. Order by directly in the select, is ignored when insert is executed.
CREATE TABLE #MyTable(Name VARCHAR(50), SortOrder INT)
INSERT INTO #MyTable
SELECT 'b', 2
UNION ALL SELECT 'c', 3
UNION ALL SELECT 'a', 1
UNION ALL SELECT 'e', 5
UNION ALL SELECT 'd', 4
SELECT Name,
ROW_NUMBER() OVER (ORDER BY MyTable.SortOrder) AS SortOrder
INTO #Result
FROM #MyTable AS MyTable
ORDER BY SortOrder
SELECT * FROM #Result
DROP TABLE #MyTable
DROP TABLE #Result

Related

How to add scope_identity() to UNION ALL syntax in SQL Server

I have a query for multiple inserts using UNION ALL.
How do I use SELECT scope_identity(); for each row of data inserted?
INSERT INTO MyTable (FirstCol, SecondCol)
SELECT 'First', 1
UNION ALL
SELECT 'Second', 2
UNION ALL
SELECT 'Third', 3
UNION ALL
SELECT 'Fourth', 4
UNION ALL
SELECT 'Fifth', 5
The short answer is no you can't. SCOPE_IDENTITY will give you the value of the last insert only. But you can use the OUTPUT clause from 2005 onwards. See https://msdn.microsoft.com/en-us/library/ms177564.aspx for information on how to use this clause.
You can use OUTPUT Clause to get Multiple Identity Column Value instead of scope_identity() while inserting Multiple Row.
Create Table tablename
(id int identity(1,5) Primary KEY, b int , c int)
DECLARE #tempIDs TABLE (id int)
INSERT tablename (b, c)
OUTPUT inserted.id INTO #tempIDs
SELECT 1, 1
UNION ALL
SELECT 2, 2
UNION ALL
SELECT 200, 3
UNION ALL
SELECT 3000, 4
UNION ALL
SELECT 400, 5
Select * from #tempIDs
Now your table variable #tempIDs have All Primary Key Data those are inserted using UNION ALL.

How can I order data and add a record to the first position of the data set?

I know I can create a temp table, insert records, order it and then use union afterwards, but I'm looking for alternative routes. I tried a cte, but I had to order the entire thing which doesn't work as my unioned record doesn't stay "on top".
Basically, I have at able with Id INT, Name VARCHAR(MAX) fields and I want to ORDER BY Name before I add an entry at the row[0] position in the return set. If I order after the union, the row I wanted at row[0] gets ordered with it.
Any ideas?
You were on the right track with a union query. Force the sort with static values.
select 0 sortfield, '' name, etc
union
select 1 sortfield, name, etc
from etc
order by sortfield, name.
CREATE TABLE #temp (
idnt INT IDENTITY(2) NOT NULL --This begins the identity col with a value of 2
,Id INT
,Name VARCHAR(MAX)
)
INSERT INTO #temp
SELECT
...
FROM myTable
ORDER BY Name
CREATE TABLE #tempAPPEND (
idnt INT IDENTITY(1) NOT NULL --This begins the identity col with a value of 1
,Id INT
,Name VARCHAR(MAX)
)
INSERT INTO #tempAPPEND (Id, Name)
VALUES ('34384','Pinal Dave') -- SAMPLE VALUES
SELECT * FROM #temp
UNION
SELECT * FROM #tempAPPEND
ORDER BY idnt

Compare one column values for the table in sql

I have table with X number of columns. One of them is nvarchar(50). Values of this column are like this:
13-46187(IC)
13-46186(IC)
13-46189
13-46185
13-46184
I want to extract/find the highest number that the column value ends with (in this case 189). How do I accomplish that?
This is hardcoded stuff. but will give you some ideas..
create table #temp
(
textfield varchar(50)
)
insert into #temp
select '13-46187(IC)'
UNION
select '13-46186(IC)'
UNION
select '13-46189'
UNION
select '13-46185'
UNION
select '13-46184'
select Max(Convert(int,substring(SUBSTRING(textfield, 6, LEN(textfield)), 1, 3)))
from #temp

Simulate a table with multiple rows just with SELECT statement

If I can do the following select statement to create a table with one value
SELECT 'myname' AS 'Name'
this will return a table with column = Name and one value = myname
how can I work around this to return one column with multiple values from just the select statement
I don't want to do this :
DECLARE #tmp TABLE (Name varchar(50))
INSERT INTO #tmp (Name) VALUES ('myname1'),('myname2')
SELECT * FROM #tmp
Just from a single SELECT statement if possible
Or, you can use the multiple VALUES in the SELECT, like:
SELECT [Name]
FROM (VALUES ('myname1'),('myname2')) AS X([name])
If you're wanting to simulate a table with multiple rows just with SELECT statement, this can typically be done with UNION of rows:
SELECT 'myname1' AS 'Name' UNION
SELECT 'myname2' UNION
SELECT 'myname3'
-- etc
Demo: http://www.sqlfiddle.com/#!3/d41d8/12433
In case you want to simulate sequential data like in your example.You can define a recursive CTE and use it like a table.
Below code will generate 10 records
;With Users as
(
Select 1 as ID, CAST('Username1' AS varchar(25)) as Name
union all
Select ID + 1 , CAST('Username'+CAST(ID+1 AS varchar(5) ) AS varchar(25))
from Users
where ID < 10
)
SELECT * FROM Users
Here is SQL Fiddle http://www.sqlfiddle.com/#!3/d41d8/12452
But CTE cannot be used in multiple statements.If you need to use it in multi statements. Then insert data from CTE to Temp Table or Table Variable.

Fixed array and random numbers in an SQL query

What's the easiest way to select one of five fixed strings returned in an SQL Server query, randomly?
I.e. the equivalent of:
function randomColumn() {
var values = ['apple', 'banana', 'orange', 'cherry', 'lemon'];
var idx = Math.floor(Math.random() * 5);
return values[idx];
}
I need to change my existing SQL script to have a certain column return one of these values, without the need to change my client code.
Do I need to create a temp table?
I'm using SQL Server 2008 R2.
You don't need a temporary table for a few strings, you can create the result on the fly:
select str
from (
select 0 as id, 'apple' as str
union all
select 1, 'banana'
union all
select 2, 'orange'
union all
select 3, 'cherry'
union all
select 4, 'lemon'
) x
where id = floor(rand() * 5)
select top 1 Value
from Table1
order by newid()
SQL Fiddle Example
Select 'apple' values
INTO #tmp
UNION ALL
Select'banana' values
UNION ALL
Select 'orange' values
.
.
.
select top 1 values
from #tmp
order by newid()
OR
ALTER TABLE #tmp
ADD id BIGINT IDENTITY(1,1)
DECLARE #rand BIGINT
SET #rand=rand()*4
select top 1 values
from #tmp
where id=#rand
DECLARE #ListofIDs TABLE(IDs VARCHAR(100), ID INT IDENTITY(1,1));
INSERT INTO #ListofIDs
SELECT 'a'
UNION ALL
SELECT 'b'
UNION ALL
SELECT '20'
UNION ALL
SELECT 'c'
UNION ALL
SELECT '30'
UNION ALL
SELECT 'd';
SELECT * FROM #ListofIDs;
SELECT Ids from #ListofIDs where ID=1+ CONVERT(INT, (5)*RAND())