By using union select for inserting values into a temp table, how to insert values in the order I assigned? - sql

I created a temp table and inserted some values into it by using union select.
CREATE TABLE tempdb..#tempName (ID int IDENTITY (1, 1) NOT NULL, Name varchar(20) NULL)
INSERT INTO #tempName(Name)
SELECT 'tommy'
UNION SELECT 'jimmy'
UNION SELECT 'adam'
UNION SELECT 'lucy'
Problem: I want to know how to insert the value as the order I wrote. Currently it inserted in Ascending order instead.
How I want the value to be inserted:
-- ID -- Name
-- 1 -- tommy
-- 2 -- jimmy
-- 3 -- adam
-- 4 -- lucy
The Order it inserted into the temp table:
-- ID -- Name
-- 1 -- adam
-- 2 -- jimmy
-- 3 -- lucy
-- 4 -- tommy

The reason you are getting things in alphabetical order is because of the union. It does additional processing to remove duplicates. That is why union all is usually recommended.
Using union all will probably almost always do what you want:
INSERT INTO #tempName(Name)
SELECT 'tommy'
UNION ALL SELECT 'jimmy'
UNION ALL SELECT 'adam'
UNION ALL SELECT 'lucy';
This approach works in practice and seems to produce a consistent execution plan that will produce the records in order. But, this behavior is not guaranteed.
Multiple INSERTs will definitely work.
INSERT INTO #tempName(Name) SELECT 'tommy';
INSERT INTO #tempName(Name) SELECT 'jimmy'
INSERT INTO #tempName(Name) SELECT 'adam'
INSERT INTO #tempName(Name) SELECT 'lucy';
And the VALUES statement will work:
INSERT INTO #tempName(Name)
VALUES (('tommy'), ('jimmy'), ('adam'), ('lucy'));

Related

Constructing single select statement that returns order depends on the value of a column in SQL Server

Table1
Id bigint primary key identity(1,1)
Status nvarchar(20)
Insert dummy data
Insert into Table1 values ('Open') --1
Insert into Table1 values ('Open') --2
Insert into Table1 values ('Grabbed') --3
Insert into Table1 values ('Closed') --4
Insert into Table1 values ('Closed') --5
Insert into Table1 values ('Open') --6
How would I construct a single select statement which orders the data where records with 'Grabbed' status is first, followed by 'Closed', followed by 'Open' in SQL Server
Output:
Id Status
3 Grabbed
4 Closed
5 Closed
1 Open
2 Open
6 Open
I think you need something like this:
select *
from yourTable
order by case when Status = 'Grabbed' then 1
when Status = 'Closed' then 2
when Status = 'Open' then 3
else 4 end
, Id;
[SQL Fiddle Demo]
Another way is to using CTE like this:
;with cte as (
select 'Grabbed' [Status], 1 [order]
union all select 'Closed', 2
union all select 'Open', 3
)
select t.*
from yourTable t
left join cte
on t.[Status] = cte.[Status]
order by cte.[order], Id;
[SQL Fiddle Demo]
This could be done much better with a properly normalized design:
Do not store your Status as a textual content. Just imagine a typo (a row with Grabed)...
Further more a lookup table allows you to add side data, e.g. a sort order.
CREATE TABLE StatusLookUp(StatusID INT IDENTITY PRIMARY KEY /*you should name your constraints!*/
,StatusName VARCHAR(100) NOT NULL
,SortRank INT NOT NULL)
INSERT INTO StatusLookUp VALUES
('Open',99) --ID=1
,('Closed',50)--ID=2
,('Grabbed',10)--ID=3
CREATE TABLE Table1(Id bigint primary key identity(1,1) /*you should name your constraints!*/
,StatusID INT FOREIGN KEY REFERENCES StatusLookUp(StatusID));
Insert into Table1 values (1) --1
Insert into Table1 values (1) --2
Insert into Table1 values (3) --3
Insert into Table1 values (2) --4
Insert into Table1 values (2) --5
Insert into Table1 values (1) --6
SELECT *
FROM Table1 AS t1
INNER JOIN StatusLookUp AS s ON t1.StatusID=s.StatusID
ORDER BY s.SortRank;
I find that the simplest method uses a string:
order by charindex(status, 'Grabbed,Closed,Open')
or:
order by charindex(',' + status + ',', ',Grabbed,Closed,Open,')
If you are going to put values in the query, I think the easiest way uses values():
select t1.*
from t1 left join
(values ('Grabbed', 1), ('Closed', 2), ('Open', 3)) v(status, priority)
on t1.status = v.status
order by coalesce(v.priority, 4);
Finally. This need suggests that you should have a reference table for statuses. Rather than putting the string name in other tables, put an id. The reference table can have the priority as well as other information.
Try this:
select Id,status from tablename where status='Grabbed'
union
select Id,status from tablename where status='Closed'
union
select Id,status from tablename where status='Open'

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.

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

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

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

inserting multiple values into a single cell

i am trying to insert multiple values into a single cell column in my db. i am using a sql statment like this:
insert into table_name (mycol) values (1A,2B,3G,4,5,6F)
this table has only one field called "mycol"
the above query doesn't run and gives me an error. however, if i run it single entry like this:
insert into table_name (mycol) values (1A)
it works!
is there a way to feed sql with multiple values?
You have to break up the values portion like so (if you're using SQL Server)
insert into table_name (mycol) values (1A),(2B),(3G),(4),(5),(6F)
Based on this article, you can do something like this:
USE YourDB
GO
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
GO
I highly recommend against storing multiple values in a single field. You're better off, normally, with having seperate fields or multiple rows...
If student X is on Courses A, B, C;
INSERT INTO mapping_table (student_id, course_id)
SELECT 123, 1
UNION ALL SELECT 123, 2
UNION ALL SELECT 123, 3
If student X has 3 lines in his address;
INSERT INTO student_address (student_id, line1, line2, line3)
SELECT 123, '10 The Street', 'The Town', 'The City'
You need to pivot the selected values or do something like this:
INSERT dbo.Table_1(mycol)
SELECT * FROM
(
SELECT '1a' AS mycol UNION
SELECT '2b' UNION
SELECT '3g' UNION
SELECT '4' UNION
SELECT '5'
) T1