Related
I need to convert my table with several fields to two other tables where one of the new tables has a row for each field in the first table in Microsoft SQL Server.
Table1(Table1Id, Field1, Field2, Field3)
for each row in Table1 create
Table2a(Table2aId)
Table2b(Table2bId, Table2aId, Field1)
Table2b(Table2bId, Table2aId, Field2)
Table2b(Table2bId, Table2aId, Field3)
Details
I currently have the table
Table1
[dbo].[CommunityAssetTemplates]
,[CommunityId]
,[CommunityAssetTemplateId]
,[BaseHouseSpecsAssetId]
,[CommunityLogoAssetId]
,[CommunityMarketingMapAssetId]
,[CommunityPhotoAssetId]
,[CommunityVideoDraftAssetId]
,[CommunityVideoAssetId]
This was mostly a quick way to fulfill a business need before we fully implemented the new feature where users can define multiple templates with different assets in them, so I made two new tables one is just to relate the second table to a Community
Table2a
[dbo].[CommunityAssetDataTemplates]
,[CommunityAssetDataTemplateId]
,[CommunityAssetTemplateTypeId]
,[CommunityId]
Table2b
[dbo].[CommunityAssetTemplateFiles]
,[CommunityAssetTemplateFileId]
,[CommunityAssetDataTemplateId]
,[CommunityAssetId]
These two tables map together like so, each Table1 row creates 1 Table2a row and 6 Table2b rows
Table2a
[CommunityAssetDataTemplateId] Auto Increments
[CommunityAssetTemplateTypeId] = 1
[CommunityId] = Table1.CommunityId
Table2b - 1
[CommunityAssetTemplateFileId] Auto increments
,[CommunityAssetDataTemplateId] = Table2a.[CommunityAssetDataTemplateId]
,[CommunityAssetId] = Table1.[BaseHouseSpecsAssetId] (THIS CHANGES)
Table2b - 2
[CommunityAssetTemplateFileId] Auto increments
,[CommunityAssetDataTemplateId] = Table2a.[CommunityAssetDataTemplateId]
,[CommunityAssetId] = Table1.[CommunityLogoAssetId] (THIS CHANGES)
continues for the remaining 4 'AssetId's fields of Table1
Here is one way to accomplish this using CROSS APPLY to separate Field1, Field2, and Field3 columns into rows:
insert into Table2A (Table2Id)
select Table1Id from Table1
insert into Table2B(Table2Id, Field4)
select Table1Id, Field
from Table1
cross apply (values(Field1), (Field2), (Field3)) as ColumnsAsRows(Field)
Here is a sample:
declare #t1 table (Table1Id int identity(1,1), Field1 int, Field2 int, Field3 int)
declare #t2 table (Table2Id int primary key clustered)
declare #t3 table (Table3Id int identity(1,1) primary key clustered, Table2Id int, Field4 int)
insert into #t1 (Field1, Field2, Field3)
values (1, 2, 3), (4, 5, 6), (7, 8, 9)
select * from #t1
insert into #t2 (Table2Id)
select Table1Id from #t1
insert into #t3 (Table2Id, Field4)
select Table1Id, Field
from #t1
cross apply (values(Field1), (Field2), (Field3)) as ColumnsAsRows(Field)
select * from #t2
select * from #t3
I've made some modifications to my database and I need to migrate the old data to the new tables. For that, I need to fill a table (ReportOptions) taking the data from the original table (Practice), and fill a second intermediate table (PracticeReportOption).
ReportOption (
ReportOptionId int PK,
field1, field2...
)
Practice (
PracticeId int PK,
field1, field2...
)
PracticeReportOption (
PracticeReportOptionId int PK,
PracticeId int FK,
ReportOptionId int FK,
field1, field2...
)
I made a query to get all the data I need to move from Practice to ReportOptions, but I'm having trouble filling the intermediate table.
--Auxiliary tables
DECLARE #ReportOption TABLE (
PracticeId int, -- This field is not on the actual ReportOption table
field1, field2...
)
DECLARE #PracticeReportOption TABLE (
PracticeId int,
ReportOptionId int,
field1, field2
)
--First I get all the data I need to move
INSERT INTO #ReportOption
SELECT P.practiceId, field1, field2...
FROM Practice P
--I insert it into the new table,
--but somehow I need to have the repation PracticeId / ReportOptionId
INSERT INTO ReportOption (field1, field2...)
OUTPUT #ReportOption.PracticeId, --> this is the field I don't know how to get
inserted.ReportOptionId
INTO #PracticeReportOption (PracticeId, ReportOptionId)
SELECT field1, field2
FROM #ReportOption
-- This would insert the relationship, If I knew how to get it!
INSERT INTO #PracticeReportOption (PracticeId, ReportOptionId)
SELECT PracticeId, ReportOptionId
FROM #ReportOption
If I could reference a field that is not from the destination table in the OUTPUT clause, that would be great (I think I can't, but I don't know for sure). Any ideas on how to accomplish my need?
You can do this by using MERGE instead of INSERT.
So replace this:
INSERT INTO ReportOption (field1, field2...)
OUTPUT #ReportOption.PracticeId, --> this is the field I don't know how to get
inserted.ReportOptionId
INTO #PracticeReportOption (PracticeId, ReportOptionId)
SELECT field1, field2
FROM #ReportOption
with:
MERGE INTO ReportOption USING #ReportOption AS temp ON 1 = 0
WHEN NOT MATCHED THEN
INSERT (field1, field2)
VALUES (temp.Field1, temp.Field2)
OUTPUT temp.PracticeId, inserted.ReportOptionId, inserted.Field1, inserted.Field2
INTO #PracticeReportOption (PracticeId, ReportOptionId, Field1, Field2);
The key is to use a predicate that will never be true (1 = 0) in the merge search condition, so you will always perform the insert, but have access to fields in both the source and destination tables.
Here is the entire code I used to test it:
CREATE TABLE ReportOption (
ReportOptionID INT IDENTITY(1, 1),
Field1 INT,
Field2 INT
)
CREATE TABLE Practice (
PracticeID INT IDENTITY(1, 1),
Field1 INT,
Field2 INT
)
CREATE TABLE PracticeReportOption (
PracticeReportOptionID INT IDENTITY(1, 1),
PracticeID INT,
ReportOptionID INT,
Field1 INT,
Field2 INT
)
INSERT INTO Practice VALUES (1, 1), (2, 2), (3, 3), (4, 4)
MERGE INTO ReportOption r USING Practice p ON 1 = 0
WHEN NOT MATCHED THEN
INSERT (field1, field2)
VALUES (p.Field1, p.Field2)
OUTPUT p.PracticeId, inserted.ReportOptionId, inserted.Field1, inserted.Field2
INTO PracticeReportOption (PracticeId, ReportOptionId, Field1, Field2);
SELECT *
FROM PracticeReportOption
DROP TABLE ReportOption
DROP TABLE Practice
DROP TABLE PracticeReportOption
More reading, and the source of all that I know on the subject is here.
Maybe someone who uses MS SQL Server 2005 or lower will find this answer useful.
MERGE will only work for SQL Server 2008 or higher.
For the rest, I found another workaround which will give you the ability to create kind of mapping tables.
Here's how Resolution will look like for SQL 2005:
DECLARE #ReportOption TABLE (
ReportOptionID INT IDENTITY(1, 1),
Field1 INT,
Field2 INT
)
DECLARE #Practice TABLE(
PracticeID INT IDENTITY(1, 1),
Field1 INT,
Field2 INT
)
DECLARE #PracticeReportOption TABLE(
PracticeReportOptionID INT IDENTITY(1, 1),
PracticeID INT,
ReportOptionID INT,
Field1 INT,
Field2 INT
)
INSERT INTO #Practice (Field1, Field2) VALUES (1, 1)
INSERT INTO #Practice (Field1, Field2) VALUES (2, 2)
INSERT INTO #Practice (Field1, Field2) VALUES (3, 3)
INSERT INTO #Practice (Field1, Field2) VALUES (4, 4)
INSERT INTO #ReportOption (field1, field2)
OUTPUT INSERTED.ReportOptionID, INSERTED.Field1, INSERTED.Field2
INTO #PracticeReportOption (ReportOptionID, Field1, Field2)
SELECT Field1, Field2
FROM #Practice
ORDER BY PracticeID ASC;
WITH CTE AS (
SELECT PracticeID,
ROW_NUMBER() OVER ( ORDER BY PracticeID ASC ) AS ROW
FROM #Practice
)
UPDATE M
SET M.PracticeID = S.PracticeID
FROM #PracticeReportOption AS M
JOIN CTE AS S ON S.ROW = M.PracticeReportOptionID
SELECT * FROM #PracticeReportOption
The main trick is that we are filling the mapping table twice with ordered data from the source and destination table.
For more details, see Merging Inserted Data Using OUTPUT in SQL Server 2005.
i have the following statement:
INSERT INTO table1 (field1, FIELD2)
SELECT f1, f2 FROM table2 -- returns 20 rows
after insert i need to know the array/table of IDs generated in table1.ID which is INT IDENTITY
thanx in advance.
Use the OUTPUT clause (SQL2005 and up):
DECLARE #IDs TABLE(ID int)
INSERT INTO table1(Field1, Field2)
OUTPUT inserted.ID into #IDs(ID)
SELECT Field1, Field2 FROM table2
If you want to know exactly which rows from table2 generated which ID in table1 (and Field1 and Field2 aren't enough to identify that), you'll need to use MERGE:
DECLARE #IDs TABLE(Table1ID int, Table2ID int)
MERGE table1 AS T
USING table2 AS S
ON 1=0
WHEN NOT MATCHED THEN
INSERT (Field1, Field2) VALUES(S.Field1, S.Field2)
OUTPUT inserted.ID, S.ID INTO #IDs(Table1ID, Table2ID)
Use SCOPE_IDENTITY()
SELECT SCOPE_IDENTITY()
http://msdn.microsoft.com/en-us/library/ms190315(v=sql.105).aspx
Given the following (Table1):
Id Field1 Field2 ...
-- ------ -------
NULL 1 2
NULL 3 4
...
I'd like to insert the values of Field1 and Field2 into a different table (Table2). Table2 has an auto increment integer primary key. I want to retrieve the new PKs from Table2 and update the Id column above (Table1).
I realize this is not conventional - its not something I need to do regularly, simply one-off for some migration work. I made some attempts using INSERT INTO, OUTPUT, INSERTED.Id, but failed. The PKs that are "looped-back" into Table1 must tie to the values of Field1/Filed2 inserted.
You should just be able to do a insert, then a delete and re-insert.
create table t1
( id int, f1 int, f2 int);
create table t2
( id int primary key IDENTITY , f1 int, f2 int);
insert into t1 (id, f1, f2) values (null, 1, 2);
insert into t1 (id, f1, f2) values (null, 3, 4);
insert into t1 (id, f1, f2) values (null, 5, 6);
insert into t1 (id, f1, f2) values (null, 5, 6);
insert into t2 (f1, f2)
select f1, f2 from t1 where id is null;
delete t1
from t1 join t2 on (t1.f1 = t2.f1 and t1.f2 = t2.f2);
insert into t1
select id, f1, f2 from t2;
select * from t1;
See this example on SQLFiddle.
You will need some type of unique key to match your rows in each table. I've taken the liberty of adding a TempGuid column to each of your tables (which you can later drop):
-- Setup test data
declare #Table1 table (
Id int null
, Field1 int not null
, Field2 int not null
, TempGuid uniqueidentifier not null unique
)
insert into #Table1 (Field1, Field2, TempGuid) select 1, 2, newid()
insert into #Table1 (Field1, Field2, TempGuid) select 3, 4, newid()
declare #Table2 table (
Id int not null primary key identity(1, 1)
, Field1 int not null
, Field2 int not null
, TempGuid uniqueidentifier not null unique
)
-- Fill Table2
insert into #Table2 (Field1, Field2, TempGuid)
select Field1, Field2, TempGuid
from #Table1
-- Update Table1 with the identity values from Table2
update a
set a.Id = b.Id
from #Table1 a
join #Table2 b on a.TempGuid = b.TempGuid
-- Show results
select * from #Table1
Output would be workable if you already had a unique key on Table1 that you were inserting into Table2. You could also do a temporary unique key (perhaps GUID again) in a loop or cursor and process one row at a time, but that seems worse to me.
UPDATE
Here is the actual SQL to run on your tables:
-- Add TempGuid columns
alter table Table1 add TempGuid uniqueidentifier null
update Table1 set TempGuid = newid()
alter table Table2 add TempGuid uniqueidentifier not null
-- Fill Table2
insert into Table2 (Field1, Field2, TempGuid)
select Field1, Field2, TempGuid
from Table1
-- Update Table1 with the identity values from Table2
update a
set a.Id = b.Id
from Table1 a
join Table2 b on a.TempGuid = b.TempGuid
-- Remove TempGuid columns
alter table Table1 drop column TempGuid
alter table Table2 drop column TempGuid
Assuming you have full control over the schema definitions, add a foreign key to Table2 that references Table1's primary key.
Perform your data insert:
INSERT INTO Table2 (Field1, Field2, T1PK)
SELECT Field1, Field2, PK FROM Table1
Then backfill Table1:
UPDATE t1 SET Id = t2.PK
FROM Table1 t1 INNER JOIN Table2 t2 ON t2.T1PK = t1.PK
Then delete the extra column (T1PK) from Table2.
Edit:
Since there's no PK in Table1, just add one to Table1, use that, and then drop it at the end.
For example...
ALTER TABLE Table1 ADD COLUMN T1PK UNIQUEIDENTIFIER CONSTRAINT Table1_PK PRIMARY KEY DEFAULT NEWID();
ALTER TABLE Table2 ADD COLUMN T1PK UNIQUEIDENTIFIER NULL
INSERT INTO Table2 (Field1, Field2, T1PK)
SELECT Field1, Field2, T1PK FROM Table1
UPDATE t1 SET Id = t2.PK
FROM Table1 t1 INNER JOIN Table2 t2 ON t2.T1PK = t1.T1PK
ALTER TABLE Table1 DROP CONSTRAINT Table1_PK
ALTER TABLE Table1 DROP COLUMN T1PK
ALTER TABLE Table2 DROP COLUMN T1PK
This is not pretty, but should do as a one time effort.
create table tableA
(
id int,
field1 int,
field2 int
)
create table tableB
(
id int identity(1,1),
field1 int,
field2 int
)
insert into tableA select NULL, 1, 2
insert into tableA select NULL, 2, 3
declare #field1_value int;
declare #field2_value int;
declare #lastInsertedId int;
DECLARE tableA_cursor CURSOR FOR
select field1, field2 from tableA
OPEN tableA_cursor
FETCH NEXT FROM tableA_cursor INTO #field1_value, #field2_value
WHILE ##FETCH_STATUS = 0
BEGIN
insert into tableB select #field1_value, #field2_value
set #lastInsertedId = (SELECT SCOPE_IDENTITY())
update a
set id = #lastInsertedId
from tableA a
where field1 = #field1_value
and field2 = #field2_value
print #field1_value
FETCH NEXT FROM tableA_cursor
INTO #field1_value, #field2_value
END
CLOSE tableA_cursor
DEALLOCATE tableA_cursor
With not exists check:
declare #field1_value int;
declare #field2_value int;
declare #lastInsertedId int;
DECLARE tableA_cursor CURSOR FOR
select field1, field2 from tableA
OPEN tableA_cursor
FETCH NEXT FROM tableA_cursor INTO #field1_value, #field2_value
WHILE ##FETCH_STATUS = 0
BEGIN
IF NOT EXISTS
(
select * from tableB
where field1 = #field1_value
and field2 = #field2_value
)
BEGIN
insert into tableB
select #field1_value, #field2_value
set #lastInsertedId = (SELECT SCOPE_IDENTITY())
update a
set id = #lastInsertedId
from tableA a
where field1 = #field1_value
and field2 = #field2_value
END
FETCH NEXT FROM tableA_cursor
INTO #field1_value, #field2_value
END
CLOSE tableA_cursor
DEALLOCATE tableA_cursor
There two tables:
Table1
field1 | field2
Table2
field1
“string1”
“string2”
I need to insert concatenation of table2.field1 values into table1, so it looks like
insert into table1(field1, field2) values (1, “string1string2”);
How can I do it? Is there any SQL-standard way to do it?
PS: string1 and string2 are values of the field1 column.
PPS: the main subtask of my question is, how can I get the result of select query into one row? All examples I've seen just use concatenation, but in all your examples SELECT subquery does not return string concatenation for all values of the table2.field1 column.
There is no ANSI standard SQL way to do this.
But in MySQL you can use GROUP_CONCAT
insert into table1 ( field1, field2 )
select 1, group_concat(field1) from table2
In SQL Server 2005 and later you can use XML PATH,
insert into table1 ( field1, field2 )
select 1, (select field1 from table2
for xml path(''), type).value('.','nvarchar(max)')
In Oracle, you can refer to Stack Overflow question How can I combine multiple rows into a comma-delimited list in Oracle?.
INSERT INTO TABLE1 (FIELD1, FILED2) VALUES (1, CONCAT("string1", "string2"))
try this :
insert into table1(field1, field2)
select table2.field1, table2.string1 || table2.string2 from table2;
You can add a where clause to the query to select only some entries from table2 :
insert into table1(field1, field2)
select table2.field1, table2.string1 || table2.string2 from table2
where table2.field = 'whatever';
I'd try with
insert table1 select field1, string1+string2 from table2
tested with MSSQL Server 2008
create table #t1 (n int, s varchar(200))
create table #t2 (n int, s1 varchar(100), s2 varchar(100))
insert #t2 values (1, 'one', 'two') -- worked without into ???
insert #t2 values (2, 'three', 'four') -- worked without into ???
insert #t1 select n, s1+s2 from #t2 -- worked without into ???
select * from #t1
drop table #t1
drop table #t2
After the edit:
No, if you have no way to identify the lines in table2 and sort them the way you want it is impossible. Remember that, in the absence of a order by in the SQL statement, lines can be returned in any order whatsoever
Assuming this is SQL server,
Insert into table1 (field1, field2)
select field1, string1 + string2
from table2
In oracle you will do it as -
Insert into table1 (field1, field2)
select field1, string1 || string2
from table2