How to insert data from one table column to another table - stored-procedures

I have a file that is something like below format.
test.txt
1 | ABC | A, B, C, D
I need a stored procedure that insert record in details table in row by row basis. e.g.
ID Name Type
1 ABC A
1 ABC B
1 ABC C
1 ABC D
Is it possible through stored procedure in sql. Any help will be appreciated. Thanks in advance.

You can either:
Split it in your code and then insert them
Bulk insert them in a temporary table and split them all like this:
-- SAMPLE Data
declare #data table(id int, name varchar(10), type varchar(100))
insert into #data(id, name, type) values
(1, 'ABCD', 'A, B, C, D')
, (2, 'EFG', 'E, F, G')
, (3, 'HI', 'H, I')
-- Split All Rows and Types
Select ID, Name, ltrim(rtrim(value))
From (
Select *, Cast('<x>'+Replace(d.type,',','</x><x>')+'</x>' As XML) As types
From #data d
) x
Cross Apply (
Select types.x.value('.', 'varchar(10)') as value
From x.types.nodes('x') as types(x)
) c
Output:
ID Name Type
1 ABCD A
1 ABCD B
1 ABCD C
1 ABCD D
2 EFG E
2 EFG F
2 EFG G
3 HI H
3 HI I

Related

Intercalate multiple columns when some of those columns must remain in the same row

In Column A I have the id of the home team, B the name of the home team, C the id of the visiting team and in D the name of the visiting team:
12345 Borac Banja Luka 98765 B36
678910 Panevezys 43214 Milsami
1112131415 Flora 7852564 SJK
1617181920 Magpies 874236551 Dila
I want to create a column of ids and another of names but keeping the sequence of who will play with whom:
12345 Borac Banja Luka
98765 B36
678910 Panevezys
43214 Milsami
1112131415 Flora
7852564 SJK
1617181920 Magpies
874236551 Dila
Currently (the model works) I'm joining the columns with a special character, using flatten and finally split:
=ARRAYFORMULA(SPLIT(FLATTEN({
FILTER(A1:A&"§§§§§"&B1:B,(A1:A<>"")*(B1:B<>"")),
FILTER(C1:C&"§§§§§"&D1:D,(C1:C<>"")*(D1:D<>""))
}),"§§§§§"))
Is there a less archaic and correct approach to working in this type of case?
Spreadsheet to tests
889
A
5687
C
532
B
8723
D
Stack up the columns using {} and SORT them by a SEQUENCE of 1,2,1,2:
=SORT({A1:B2;C1:D2},{SEQUENCE(ROWS(A1:B2));SEQUENCE(ROWS(A1:B2))},1)
889
A
5687
C
532
B
8723
D
You can also try with function QUERY, enter this formula in F1:
={QUERY((A1:B), "SELECT * WHERE A IS NOT NULL and B IS NOT NULL",1);
QUERY((C1:D), "SELECT * WHERE C IS NOT NULL and D IS NOT NULL",1)}

SPSS Restructure Data

I have data in the following format:
ID Var1
1 a
1 a
1 b
1 b
2 c
2 c
2 c
I'd like to convert it (restructure it) to the following format in SPSS:
ID Var1_1 Var1_2 Var1_3 Total_Count
1 n(a)=2 n(b)=2 n( c )=0 4
2 n(a)=0 n(b)=0 n( c )=3 3
First I'll create some fake data to work with:
data list list/ID (f1) Var1 (a1).
begin data
1 a
1 a
1 b
1 b
2 c
2 c
2 c
3 b
3 c
3 c
3 c
end data.
dataset name ex.
Now you can run the following - aggregate, restructure, create the string with the counts:
aggregate outfile=* /break ID Var1/n=n.
sort cases by ID Var1.
casestovars /id=ID /index=var1.
recode a b c (miss=0).
string Var1_1 Var1_2 Var1_3 (a10).
do repeat abc=a b c/Var123=Var1_1 Var1_2 Var1_3/val="a" "b" "c".
compute Var123=concat("n(", val, ")=", ltrim(string(abc, f3))).
end repeat.
compute total_count=sum(a, b, c).
If you're doing this in SPSS Modeler, here is a stream image that works for this. The order is:
Create Data Set using User Input node, setting ID to integer and Var1 to string
Restructure by Var1 values to generate field Var1_a, Var1_b, and Var1_c
Aggregate using key field ID to sum counts Var1_a, Var1_b, and Var1_c, allowing Record Count field to be generated
Output to Table
Restructure and Aggregate in SPSS Modeler
The transpose node comes in handy if you use version 18.1.
As it is a simple pivot, you can go to "Fields and Records", then place the ID in "Index", Var1 in "Fields" and see if you can add another field for Count aggregation. If not, just derive it.

Updating inserted record within MERGE statement in SQL Server 2008 R2

I have following code in my SQL Server 2008 R2 stored procedure. In that stored procedure, I am copying one city to another city with it's family and persons.
Here I maintain family's source and target id in #FamilyIdMap.
left column indicates the codes line no.
-- Copy Person
1> DECLARE #PersonIdMap table (TargetId int, SourceId int)
2> MERGE Person as PersonTargetTable
3> USING (SELECT PersonID, FamilyID, PersonName, ParentID FROM Person
4> WHERE FamilyID in (SELECT FamilyID from Family where FamilyName like '%DA%'))
5> AS PersonSourceTable ON (0=1)
6> WHEN NOT MATCHED THEN
7> INSERT(FamilyID, PersonName, ParentID)
8> VALUES
9> ((SELECT TOP 1 TargetID from #FamilyIdMap WHERE SourceID=FamilyID),PersonName,
10> ParentID) OUTPUT
11> INSERTED.PersonID, PersonSourceTable.PersonID
12> INTO #PersonIdMap;
It gives the output like this:
Source Table
PersonID FamilyID PersonName ParentID
1 1 ABC Null
2 1 Son of ABC 1
3 1 Son of ABC 1
4 2 XYZ NULL
5 2 Son of XYZ 4
Target Table (Copied from Source Table using above given code)
PersonID FamilyID PersonName ParentID
6 1 ABC Null
7 1 Son of ABC 1 <-- ParentID Remains as it is
8 1 Son of ABC 1 <--
9 2 XYZ NULL
10 2 Son of XYZ 4 <--
Problem in above output is it doesn't update the parentID, I want the output to be this:
Expected Target Table
PersonID FamilyID PersonName ParentID
6 1 ABC Null
7 1 Son of ABC 6 <-- ParentID should be updated
8 1 Son of ABC 6 <--
9 2 XYZ NULL
10 2 Son of XYZ 9 <--
I know problem is at line # 10 of code
10> ParentID) OUTPUT
but what should I replace with ParentID to update it ? Thanks in advance.
What you are trying to do cannot be done in a single step in SQL Server 2008R2.
Updating the ParentId has to be a second step, as you cannot access OUTPUT values in one row that where the result of the insert of another row. However, you are already collecting the information for the second step. So, you just need to add a simple update.
IF OBJECT_ID('dbo.Person') IS NOT NULL DROP TABLE dbo.Person;
IF OBJECT_ID('dbo.Family') IS NOT NULL DROP TABLE dbo.Family;
CREATE TABLE dbo.Family(FamilyID INT IDENTITY(1,1) PRIMARY KEY CLUSTERED, FamilyName NVARCHAR(60));
CREATE TABLE dbo.Person(PersonID INT IDENTITY(1,1) PRIMARY KEY CLUSTERED, FamilyID INT REFERENCES dbo.Family(FamilyID), PersonName NVARCHAR(60), ParentID INT);
INSERT INTO dbo.Family(FamilyName) VALUES
('DA1'),
('DA2');
INSERT INTO dbo.Person(FamilyID, PersonName, ParentID) VALUES
(1, 'ABC', NULL),
(1, 'Son of ABC', 1),
(1, 'Son of ABC', 1),
(2, 'XYZ', NULL),
(2, 'Son of XYZ', 4 );
DECLARE #FamilyIdMap table (TargetId int, SourceId int)
MERGE dbo.Family tf
USING (SELECT * FROM dbo.Family WHERE FamilyName like '%DA%') AS sf
ON 1=0
WHEN NOT MATCHED THEN
INSERT (FamilyName)
VALUES(sf.FamilyName)
OUTPUT INSERTED.FamilyID, sf.FamilyID
INTO #FamilyIdMap;
DECLARE #PersonIdMap table (TargetId int, SourceId int)
MERGE dbo.Person as tp
USING (SELECT p.PersonID, p.FamilyID, p.PersonName, p.ParentID, fm.SourceId,fm.TargetId FROM Person AS p
INNER JOIN #FamilyIdMap AS fm
ON p.FamilyID = fm.SourceId) AS sp
ON (0=1)
WHEN NOT MATCHED THEN
INSERT(FamilyID, PersonName, ParentID)
VALUES
(sp.TargetId,PersonName, ParentID) OUTPUT
INSERTED.PersonID, sp.PersonID
INTO #PersonIdMap;
UPDATE p SET
ParentID = pm.TargetId
FROM dbo.Person AS p
JOIN #PersonIdMap pm
ON pm.SourceId = p.ParentID
WHERE EXISTS(SELECT 1 FROM #PersonIdMap pmf WHERE pmf.TargetId = p.PersonID);
SELECT * FROM dbo.Family;
SELECT * FROM #FamilyIdMap;
SELECT * FROM dbo.Person;
SELECT * FROM #PersonIdMap;
I did add code to create and fill the #FamilyIdMap table. I also cleaned up your original MERGE a little. It is now using the #FamilyIdMap table as a means to select the rows instead of joining to the dbo.Family table again. If you run this only on a small subset of families this should be faster. If you have a lot of families and you copy them all, going against the dbo.Family table again might be faster.
The final UPDATE updates only new rows in the Person table (all newly created PersonIds can be found in the TargetId column of the #PersonIdMap table), changing old ParentId values to new ParentId values using the information in the #PersonIdMap table.
I did not include transaction management, but atleast the MERGE dbo.Person and the following UPDATE dbo.Person should be executed inside the same transaction.

sqlite2: Joining max values per column from another table (subquery reference)?

I'm using the following database:
CREATE TABLE datas (d_id INTEGER PRIMARY KEY, name_id numeric, countdata numeric);
INSERT INTO datas VALUES(1,1,20); //(NULL,1,20);
INSERT INTO datas VALUES(2,1,47); //(NULL,1,47);
INSERT INTO datas VALUES(3,2,36); //(NULL,2,36);
INSERT INTO datas VALUES(4,2,58); //(NULL,2,58);
INSERT INTO datas VALUES(5,2,87); //(NULL,2,87);
CREATE TABLE names (n_id INTEGER PRIMARY KEY, name text);
INSERT INTO names VALUES(1,'nameA'); //(NULL,'nameA');
INSERT INTO names VALUES(2,'nameB'); //(NULL,'nameB');
What I would like to do, is to select all values (rows) of names - to which all columns of datas will be appended, for the row where datas.countdata is maximum for n_id (and of course, where name_id = n_id).
I can somewhat get there with the following query:
sqlite> .header ON
sqlite> SELECT * FROM names AS n1
LEFT OUTER JOIN (
SELECT d_id, name_id, countdata FROM datas AS d1
WHERE d1.countdata IN (
SELECT MAX(countdata) FROM datas
WHERE name_id=1
)
) AS p1 ON n_id=name_id;
n1.n_id|n1.name|p1.d_id|p1.name_id|p1.countdata
1|nameA|2|1|47
2|nameB|||
... however - obviously - it only works for a single row (the one explicitly set by name_id=1).
The problem is, the SQL query fails whenever I try to somehow reference the "current" n_id:
sqlite> SELECT * FROM names AS n1
LEFT OUTER JOIN (
SELECT d_id, name_id, countdata FROM datas AS d1
WHERE d1.countdata IN (
SELECT MAX(countdata) FROM datas
WHERE name_id=n1.n_id
)
) AS p1 ON n_id=name_id;
SQL error: no such column: n1.n_id
Is there any way of achieving what I want in Sqlite2??
Thanks in advance,
Cheers!
Oh, well - that wasn't trivial at all, but here is a solution:
sqlite> SELECT * FROM names AS n1
LEFT OUTER JOIN (
SELECT d1.*
FROM datas AS d1, (
SELECT max(countdata) as countdata,name_id
FROM datas
GROUP BY name_id
) AS ttemp
WHERE d1.name_id = ttemp.name_id AND d1.countdata = ttemp.countdata
) AS p1 ON n1.n_id=p1.name_id;
n1.n n1.name p1.d_id p1.name_id p1.countdata
---- ------------ ---------- ---------- -----------------------------------
1 nameA 2 1 47
2 nameB 5 2 87
Well, hope this ends up helping someone, :)
Cheers!
Notes: note that just calling max(countdata) screws up competely d_id:
sqlite> select d_id,name_id,max(countdata) as countdata from datas group by name_id;
d_id name_id countdata
---- ------------ ----------
3 2 87
1 1 47
so to get correct corresponding d_id, we must do max() on datas separately - and then perform sort of an intersect with the full datas (except that intersect in sqlite requires that there are equal number of columns in both datasets, which is not the case here - and even if we made it that way, as seen above d_id will be wrong, so intersect will not work).
One way to do that is in using a sort of a temporary table, and then utilize a multiple table SELECT query so as to set conditions between full datas and the subset returned via max(countdata), as shown below:
sqlite> CREATE TABLE ttemp AS SELECT max(countdata) as countdata,name_id FROM datas GROUP BY name_id;
sqlite> SELECT d1.*, ttemp.* FROM datas AS d1, ttemp WHERE d1.name_id = ttemp.name_id AND d1.countdata = ttemp.countdata;
d1.d d1.name_id d1.countda ttemp.coun ttemp.name_id
---- ------------ ---------- ---------- -----------------------------------
2 1 47 47 1
5 2 87 87 2
sqlite> DROP TABLE ttemp;
or, we can rewrite the above so a SELECT subquery (sub-select?) is used, like this:
sqlite> SELECT d1.* FROM datas AS d1, (SELECT max(countdata) as countdata,name_id FROM datas GROUP BY name_id) AS ttemp WHERE d1.name_id = ttemp.name_id AND d1.countdata = ttemp.countdata;
d1.d d1.name_id d1.countda
---- ------------ ----------
2 1 47
5 2 87

MySQL query that computes partial sums

What query should I execute in MySQL database to get a result containing partial sums of source table?
For example when I have table:
Id|Val
1 | 1
2 | 2
3 | 3
4 | 4
I'd like to get result like this:
Id|Val
1 | 1
2 | 3 # 1+2
3 | 6 # 1+2+3
4 | 10 # 1+2+3+4
Right now I get this result with a stored procedure containing a cursor and while loops. I'd like to find a better way to do this.
You can do this by joining the table on itself. The SUM will add up all rows up to this row:
select cur.id, sum(prev.val)
from TheTable cur
left join TheTable prev
on cur.id >= prev.id
group by cur.id
MySQL also allows the use of user variables to calculate this, which is more efficient but considered something of a hack:
select
id
, #running_total := #running_total + val AS RunningTotal
from TheTable
SELECT l.Id, SUM(r.Val) AS Val
FROM your_table AS l
INNER JOIN your_table AS r
ON l.Val >= r.Val
GROUP BY l.Id
ORDER By l.Id

Resources