Looping over the insert data into multiple tables - sql

I have a query where I need to run to do manual inserts
I can do it but there are many records and was looking if I can build something.
I have a structure somewhat like this:
Have 4 id of a table - primary key values as:
var ids = "1,2,3,4";
loop over ids {
insert into table1(col1,col2,col3)
select col1,newid(),getdate() from table1 where id = ids - 1 at a time
var selectedID = get the id of the inserted row and then insert into anotehr table as:
insert into table2(col1,col2,col3,col4)
select selectedID, getdate(),getdate(),4 from table2 where fkID = ids - one at a time
}

You can use both loops and cursors but often they can be avoided.
Is there a specific reason you note you want them inserted one at a time? An alternative would be to have the IDs staged, in a temp table, or CTE, e.g.
;WITH [Ids] AS
(
SELECT '1' AS [ID]
UNION
SELECT '2'
UNION
SELECT '3'
UNION
SELECT '4'
)
INSERT INTO [Table1]
(
[Col1],
[Col2],
[Col3]
)
SELECT [Col1],
NEWID(),
GETDATE()
FROM [Table1] T
INNER JOIN [Ids] I ON I.[ID] = T.[Id];
Which avoids the need for any loops, and should perform much better.
Edit
The way I would structure this, to make the query reusable would be as follows:
IF OBJECT_ID('tempdb..#IDS') IS NOT NULL
BEGIN
DROP TABLE #IDS
END
IF OBJECT_ID('tempdb..#Inserted_IDS') IS NOT NULL
BEGIN
DROP TABLE #Inserted_IDS
END
CREATE TABLE #IDS
(
ID INT
);
CREATE TABLE #Inserted_IDS
(
ID INT,
);
INSERT INTO #IDS
(
ID
)
SELECT 1 UNION
SELECT 2 UNION
SELECT 3 UNION
SELECT 4;
INSERT INTO [Table1]
(
[Col1],
[Col2],
[Col3]
)
OUTPUT Inserted.ID
INTO #Inserted_IDS
SELECT [Col1],
NEWID(),
GETDATE()
FROM [Table1] T
INNER JOIN #IDS I ON I.[ID] = T.[Id];
INSERT INTO [table2]
(
[col1],
[col2],
[col3],
[col4]
)
SELECT I.[ID],
getdate(),
getdate(),
4
FROM [#Inserted_IDS] I
DROP TABLE #IDS;
DROP TABLE #Inserted_IDS;
Therefore you only need to amend the IDs being entered into the temp table each time you need to do the inserts.

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'

Inserting temp table values into a table.

I have a temp table declared
declare #tmptable(
value nvarchar(500) not null
);
I use a function to insert values into that temp table.
I am trying to figure out how to update a table using the values of #tmptable
insert into t1 (
active
,SchoolId
,inserted
)
select
1
,temp.value
,#insertedDate
select temp.value from #tmptable;
When i try to insert in table t1 it doesn't work. I guess there are two Select statements is causing the problem. Please let me know how to fix it. Thanks
Try this one -
INSERT INTO dbo.t1
(
Active
, SchoolId
, Inserted
)
SELECT
1
, t.value
, #insertedDate
FROM #tmptable t;
INSERT INTO t1
(
ACTIVE
,SchoolId
,INSERTED
)
SELECT 1
,temp.value
,#insertedDate
FROM #tmptable temp;
insert into t1 (
active
,SchoolId
,inserted
)
select
1
,temp.value
,#insertedDate
from #tmptable;
this will work...

Union temporary tables to a final temporary table

I have like 10 diff temporary tables created in SQL server, what I am looking to do is union them all to a final temporary table holding them all on one table. All the tables have only one row and look pretty much exactly like the two temp tables below.
Here is what I have so far this is an example of just two of the temp tables as their all exactly like this one then #final is the table I want to union the all to:
create table #lo
(
mnbr bigint
)
insert into #login (mnbr)
select distinct (_ID)
FROM [KDB].[am_LOGS].[dbo].[_LOG]
WHERE time >= '2012-7-26 9:00:00
Select count(*) as countreject
from #lo
create table #pffblo
(
mber_br
)
insert into #pffblo (mber_br)
select distinct (mber_br)
from individ ip with (nolock)
join memb mp with (nolock)
on( ip.i_id=mp.i_id and mp.p_type=101)
where ip.times >= '2012-9-26 11:00:00.000'
select count(*) as countaccept
create table #final
(
countreject bigint
, Countacceptbigint
.....
)
insert into #final (Countreject, Countaccept....more rows here...)
select Countreject, Countaccept, ...more rows selected from temp tables.
from #final
union
(select * from #lo)
union
(select * from #pffblo)
select *
from #final
drop table #lo
drop table #pffblo
drop table #final
if this the form to union the rows form those temp tables to this final one. Then is this correct way to show all those rows that were thus unioned. When I do this union I get message number of columns in union need to match number of columns selected in union
I think you're using a union the wrong way. A union is used when you have to datasets that are the same structure and you want to put them into one dataset.
e.g.:
CREATE TABLE #Table1
(
col1 BIGINT
)
CREATE TABLE #Table2
(
col1 BIGINT
)
--populate the temporary tables
CREATE TABLE #Final
(
col1 BIGINT
)
INSERT INTO #Final (col1)
SELECT *
FROM #Table1
UNION
SELECT *
FROM #Table2
drop table #table1
drop table #table2
drop table #Final
I think what you're trying to do is get 1 data set with the count of all your tables in it. Union won't do this.
The easiest way (although not very performant) would be to do select statements like the following:
CREATE TABLE #Table1
(
col1 BIGINT
)
CREATE TABLE #Table2
(
col1 BIGINT
)
--populate the temporary tables
CREATE TABLE #Final
(
col1 BIGINT,
col2 BIGINT
)
INSERT INTO #Final (col1, col2)
select (SELECT Count(*) FROM #Table1) as a, (SELECT Count(*) FROM #Table2) as b
select * From #Final
drop table #table1
drop table #table2
drop table #Final
It appears that you want to take the values from each of temp tables and then place then into a single row of data. This is basically a PIVOT, you can use something like this:
create table #final
(
countreject bigint
, Countaccept bigint
.....
)
insert into #final (Countreject, Countaccept....more rows here...)
select
from
(
select count(*) value, 'Countreject' col -- your UNION ALL's here
from #lo
union all
select count(*) value, 'countaccept' col
from #pffblo
) x
pivot
(
max(value)
for col in ([Countreject], [countaccept])
) p
Explanation:
You will create a subquery similar to this that will contain the COUNT for each of your individual temp table. There are two columns in the subquery, one column contains the count(*) from the table and the other column is the name of the alias:
select count(*) value, 'Countreject' col
from #lo
union all
select count(*) value, 'countaccept' col
from #pffblo
You then PIVOT these values to insert into your final temp table.
If you do not want to use PIVOT, then you can use a CASE statement with an aggregate function:
insert into #final (Countreject, Countaccept....more rows here...)
select max(case when col = 'Countreject' then value end) Countreject,
max(case when col = 'countaccept' then value end) countaccept
from
(
select count(*) value, 'Countreject' col -- your UNION ALL's here
from #lo
union all
select count(*) value, 'countaccept' col
from #pffblo
) x
Or you might be able to JOIN all of the temp tables similar to this, where you create a row_number() for the one record in the table and then you join the tables with the row_number():
insert into #final (Countreject, Countaccept....more rows here...)
select isnull(lo.Countreject, 0) Countreject,
isnull(pffblo.Countaccept, 0) Countaccept
from
(
select count(*) Countreject,
row_number() over(order by (SELECT 0)) rn
from #lo
) lo
left join
(
select count(*) Countaccept,
row_number() over(order by (SELECT 0)) rn
from #pffblo
) pffblo
on lo.rn = pffblo.rn
SELECT *
INTO #1
FROM TABLE2
UNION
SELECT *
FROM TABLE3
UNION
SELECT *
FROM TABLE4
If you would like to get count for each temporary table in the resulting table, you will need just to calculate it for each column in subquery:
INSERT INTO result (col1, col2,...
SELECT
(SELECT COUNT() FROM tbl1) col1
,(SELECT COUNT() FROM tbl2) col2
..

Full Outer Join Using Each Row Once

I'm wondering if anyone's come across a neat solution to this problem. I'm trying to select data from a couple of tables, having the records match up row by row. I'm basically after a full outer join, but there's one crucial difference. If I have four rows with a particular value in the column I'm joining on in one table, and three rows with this value in another, I only want the first three results to be joined, and the fourth to act as if there had been no match.
The reason for this is to create a reconciliation report which ensures transactions are not counted multiple times when comparing results. I can get around this issue by using a bit of grouping and some aggregate functions, but this hides some of the detail which I'd like to keep.
Below is an example to show the sort of thing I'm after, with the invalid/pseudo code in the comments illustrating how I'm thinking of this as working:
declare #t1 table (id bigint identity(1,1) primary key clustered, foreignKeyId bigint, otherData nvarchar(10))
declare #t2 table (id bigint identity(1,1) primary key clustered, foreignKeyId bigint, moreData nvarchar(10))
insert #t1 select 1, '1.1.1'
union all select 1, '1.1.2'
union all select 1, '1.1.3'
union all select 3, '1.3.1'
union all select 3, '1.3.2'
union all select 3, '1.3.3'
union all select 4, '1.4.3'
insert #t2 select 1, '2.1.1'
union all select 1, '2.1.2'
union all select 1, '2.1.3'
union all select 2, '2.2.1'
union all select 3, '2.3.1'
union all select 3, '2.3.2'
union all select 5, '2.5.1'
union all select 5, '2.5.2'
--demo of the functionality i'm hoping to acheive
--
/*
select t1.id id1
, t2.id id2
, t1.foreignKeyId fk1
, t2.foreignKeyId fk2
, t1.otherData otherData
, t2.moreData moreData
from #t1 t1
full funky join #t2 t2
on t1.foreignKeyId = t2.foreignKeyId
order by t1.id, t2.id --we'd need an order by to ensure the match could be applied in a predictable manner
*/
--
declare #funkyjoin table (id1 bigint, id2 bigint, fk1 bigint, fk2 bigint, otherData nvarchar(10), moreData nvarchar(10))
declare #id1 bigint, #id2 bigint
insert #funkyjoin (id1, fk1, otherData)
select id, foreignKeyId, otherData from #t1
while exists(select 1 from #t2)
begin
select top 1 #id2 = id from #t2 order by id
set #id1 = null
select top 1 #id1 = id1
from #funkyjoin
where fk2 is null
and fk1 in (select foreignKeyId from #t2 where id = #id2)
if #id1 is null
begin
insert #funkyjoin (id2, fk2, moreData)
select id, foreignKeyId, moreData
from #t2
where id = #id2
end
else
begin
update #funkyjoin
set id2 = #id2
, fk2 = fk1 --since we're joining on this we can just match it
, moreData = (select moreData from #t2 where id = #id2)
where id1 = #id1
end
delete from #t2 where id = #id2 --since this is only an example let's not worry about keeping our source data
end
select *
from #funkyjoin
order by coalesce(id1, id2)
I've written a similar solution for when this scenario occurs on spreadsheets previously: http://officemacros.codeplex.com/#WorksheetMergeMacro
If I understand correctly, this may be what you're after:
select *
from (
select *,
row_number() over (partition by foreignKeyId order by id) as n
from #t1
) t1
full outer join (
select *,
row_number() over (partition by foreignKeyId order by id) as n
from #t2
) t2 on t1.foreignKeyId = t2.foreignKeyId and t1.n = t2.n
The best way to use up the rows is to add a pseudo-row number (using ROW_NUMBER) and include that in the join.

deleting duplicate rows?

i want to delete duplicate rows from my table on the basis of category ID, but don't want to delete all, i want to left one rows if there are more than one row with the same category ID.
this is my query i am making i need to change it.
delete from twinhead_tblcategory where categoryid in (select categoryid from twinhead_tblcategory group by categoryid having count(categoryid) > 1 )
For SQL Server you can do it:
WITH MyTableCTE (CategoryId, RowNumber)
AS
(
SELECT CategoryId, ROW_NUMBER() OVER (ORDER BY CategoryId) AS 'RowNumber'
FROM MyTable
)
Delete From MyTableCTE Where RowNumber > 1
Do a select distinct into a new table, delete the old one and rename the new one into old table name.
If your rows have a distinct id column, then this should work:
DELETE t1 FROM your_table t1, your_table t2
WHERE t1.column1 = t2.column1 AND t1.column2 = t2.column2
AND ... /* check equality of all relevant columns */
AND t1.id < t2.id
Check here for sql server - http://support.microsoft.com/kb/139444 - that should get you started.
This is probably heavy-handed but perhaps you could select distinct * into a temp table, then truncate the table, then insert into the table the contents of the temp table. Foreign key constraints may prevent this, though.
For SqlServer, you could use a cursor to loop through all items, ordered by that categoryID.
Is the current ID the same as the previous one? Then delete it, see example C of this article.
Else remember the ID for the next round.
You have several way for delete duplicate rows.
for my solutions , first consider this table for example
CREATE TABLE #Employee
(
ID INT,
FIRST_NAME NVARCHAR(100),
LAST_NAME NVARCHAR(300)
)
INSERT INTO #Employee VALUES ( 1, 'Vahid', 'Nasiri' );
INSERT INTO #Employee VALUES ( 2, 'name1', 'lname1' );
INSERT INTO #Employee VALUES ( 3, 'name2', 'lname2' );
INSERT INTO #Employee VALUES ( 2, 'name1', 'lname1' );
INSERT INTO #Employee VALUES ( 3, 'name2', 'lname2' );
INSERT INTO #Employee VALUES ( 4, 'name3', 'lname3' );
First solution : Use another table for duplicate rows.
SELECT DISTINCT *
FROM #Employee
SELECT * INTO #DuplicateEmployee
FROM #Employee
INSERT #DuplicateEmployee
SELECT DISTINCT *
FROM #Employee
BEGIN TRAN
DELETE #Employee
INSERT #Employee
SELECT *
FROM #DuplicateEmployee
COMMIT TRAN
DROP TABLE #DuplicateEmployee
SELECT DISTINCT *
FROM #Employee
Second solution :
SELECT DISTINCT * FROM #Employee
SELECT * INTO #DuplicateEmployee FROM #Employee
INSERT #DuplicateEmployee
SELECT ID,
FIRST_NAME,
LAST_NAME
FROM #Employee
GROUP BY
ID,FIRST_NAME,LAST_NAME
HAVING COUNT(*) > 1
BEGIN TRAN
DELETE #Employee
FROM #DuplicateEmployee
WHERE #Employee.ID = #DuplicateEmployee.ID
AND #Employee.FIRST_NAME = #DuplicateEmployee.FIRST_NAME
AND #Employee.LAST_NAME = #DuplicateEmployee.LAST_NAME
INSERT #Employee
SELECT *
FROM #DuplicateEmployee
COMMIT TRAN
DROP TABLE #DuplicateEmployee
SELECT DISTINCT * FROM #Employee
teared solution : use rowcount
SELECT DISTINCT *
FROM #Employee
SET ROWCOUNT 1
SELECT 1
WHILE ##rowcount > 0
DELETE #Employee
WHERE 1 < (
SELECT COUNT(*)
FROM #Employee a2
WHERE #Employee.ID = a2.ID
AND #Employee.FIRST_NAME = a2.FIRST_NAME
AND #Employee.LAST_NAME = a2.LAST_NAME
)
SET ROWCOUNT 0
SELECT DISTINCT *
FROM #Employee
Fourth solution : use Analytical Functions
SELECT DISTINCT *
FROM #Employee;
WITH #DeleteEmployee AS (
SELECT ROW_NUMBER()
OVER(PARTITION BY ID, First_Name, Last_Name ORDER BY ID) AS
RNUM
FROM #Employee
)
DELETE
FROM #DeleteEmployee
WHERE RNUM > 1
SELECT DISTINCT *
FROM #Employee
Fifth solution : Use identity field
SELECT DISTINCT *
FROM #Employee;
ALTER TABLE #Employee ADD UNIQ_ID INT IDENTITY(1, 1)
DELETE
FROM #Employee
WHERE UNIQ_ID < (
SELECT MAX(UNIQ_ID)
FROM #Employee a2
WHERE #Employee.ID = a2.ID
AND #Employee.FIRST_NAME = a2.FIRST_NAME
AND #Employee.LAST_NAME = a2.LAST_NAME
)
ALTER TABLE #Employee DROP COLUMN UNIQ_ID
SELECT DISTINCT *
FROM #Employee
and end of all solution use this command
DROP TABLE #Employee
Source of my answer is this site