join many rows into one xml column - sql-server

I have two tables like this:
and
The first generated table lists all the actors/directors/other and the second table lists all the movies. What I need to do is fit all the Person_ID and Name into one column in the second table, ideally through a join and XML conversion. I know how to convert a whole table to an XML, but I am not sure how to do it to a dozen rows on the fly, is iteration the way? Any ideas?

If you use FOR XML PATH('') it eliminates the root tags. If you do not specify a column alias, it will just output the value. So you can create a string of values in a sub-query as one of the column values. Something like this should get you started:
create table #Cast (FilmID int, Person_ID int, PersonName varchar(20))
create table #Films (FilmID int , FilmName varchar(20))
insert into #Cast values (1, 1, 'bob')
insert into #Cast values (1, 2, 'tom')
insert into #Cast values (2, 3, 'sam')
insert into #Cast values (2, 4, 'ray')
insert into #Films values (1, 'Flowers for Charlie')
insert into #Films values (2, 'Batman')
SELECT #Films.*,
SUBSTRING(
(SELECT ( ',' + convert(varchar(10),Person_ID) + ',' + PersonName)
FROM #Cast
WHERE #Films.FilmID = #Cast.FilmID
ORDER BY #Films.FilmID, #Cast.FilmID
FOR XML PATH('')
), 2, 8000) CastList
FROM #Films

Related

Get max value from table and group by with column having comma separated value with different order or having more values

I am having a table like this along with data
CREATE TABLE temp (
`name` varchar(20),
`ids` varchar(20),
`value1` int,
`value2` int
);
INSERT INTO temp(`name`,`ids`, `value1`, `value2`) values
('A', '1,2', 10, 11),
('A', '2,1', 12, 100),
('A', '1,2,3', 20, 1),
('B', '6', 30, 10)
I need to get the max value by Name along with ids
I am using the following query to get the max value.
select name, ids, max(value1) as value1, max(value2) as value2
from temp
group by name,ids
The question has been tagged as Sybase ASE, but the 'create table and 'insert' commands are invalid in ASE so not sure if this is an issue of an incorrect tag or the wrong 'create table' and 'insert' commands ... so, assuming this is for a Sybase ASE database:
I'm assuming the desired output is to display those rows where value = max(value).
First we'll setup our test case:
create table mytab
(name varchar(20)
,ids varchar(20)
,value int)
go
insert into mytab (name,ids,value) values ('A', '1,2' , 10)
insert into mytab (name,ids,value) values ('A', '2,1' , 12)
insert into mytab (name,ids,value) values ('A', '1,2,3', 20)
insert into mytab (name,ids,value) values ('B', '6' , 30)
go
Here's one possible solution:
select t.name, t.ids, t.value
from mytab t
join (select name,max(value) as maxvalue from mytab group by name) dt
on t.name = dt.name
and t.value = dt.maxvalue
order by t.name
go
name ids value
-------------------- -------------------- -----------
A 1,2,3 20
B 6 30
The subquery/derived-table gives us the max(value) for each unique name. The main query then joins these name/max(value) pairs back to the main table to give us the desired rows (ie, where value = max(value)).
Tested on ASE 15.7 SP138.

Recursive SQL query with multiple columns

I have a table with the following columns
idRelationshipType int,
idPerson1 int,
idPerson2 int
This table allows me to indicate records in a database that should be linked together.
I need to do a query returning all the unique ids where a person's id exists in idPerson1 or idPerson2 columns. Additionally, I need the query to be recursive so that the if I a match is found in idPerson1, the value for idPerson2 is included in the result set and used to repeat the query recursively until no more matches are found.
Example data:
CREATE TABLE [dbo].[tbRelationships]
(
[idRelationshipType] [int],
[idPerson1] [int] ,
[idPerson2] [int]
)
INSERT INTO tbRelationships (idRelationshipType, idPerson1, idPerson2)
VALUES (1, 1, 2)
INSERT INTO tbRelationships (idRelationshipType, idPerson1, idPerson2)
VALUES (1, 2, 3)
INSERT INTO tbRelationships (idRelationshipType, idPerson1, idPerson2)
VALUES (1, 3, 4)
INSERT INTO tbRelationships (idRelationshipType, idPerson1, idPerson2)
VALUES (1, 5, 1)
Four 'Relationships' are defined here. For this query, I will only know one of the ids to begin with. I need a query that in concept works like
SELECT idPerson
FROM [some query]
WHERE [the id i have to start with] = #idPerson
AND idRelationshipType = #idRelationshipType
The returned result should be a 5 rows with one column 'idPerson', with 1, 2, 3, 4, and 5 as the row values.
I have tried various combinations of UNPIVOT and recursive CTEs but I am not making much progress.
Any help would be greatly appreciated.
Thanks,
Daniel
I think this is what you want:
DECLARE #RelationshipType int
DECLARE #PersonId int
SELECT #RelationshipType = 1, #PersonId = 1
;WITH Hierachy (idPerson1, IdPerson2)
AS
(
--root
SELECT R.idPerson1, R.idPerson2
FROM tbRelationships R
WHERE R.idRelationshipType = #RelationshipType
AND (R.idPerson1 = #PersonId OR R.idPerson2 = #PersonId)
--recurse
UNION ALL
SELECT R.idPerson1, R.idPerson2
FROM Hierachy H
JOIN tbRelationships R
ON (R.idPerson1 = H.idPerson2
OR R.idPerson2 = H.idPerson1)
AND R.idRelationshipType = #RelationshipType
)
SELECT DISTINCT idPerson
FROM
(
SELECT idPerson1 AS idPerson FROM Hierachy
UNION
SELECT idPerson2 AS idPerson FROM Hierachy
) H
Essentially, get the first rows where the required id is in either column, and then recurse getting all of the child ids based on id column 2

Comma Separated String from Junction Table Inner Query (SQL Server)

I've searched many threads, sites, and blogs, as well as the API and can't seem to figure this one out. I want to create a CSV string from rows in a junction table, but here's the catch. It needs to be for an entire set of data. I know, confusing, here's the breakdown.
Let's use the generic example with the following tables:
Students (ID, Name, Gpa)
Classes (ID, Dept, Level)
StudentsInClasses (StudentID, ClassID)
Say I want to query for all classes, but within that query (for each record) I want to also get a CSV string of the Student IDs in each of the classes. The results should look something like this:
Class StudentsID_CSVString
----- --------------------
BA302 1,2,3,4,5,6,7,8,9
BA479 4,7,9,12,15
BA483 7,9,12,18
I tried to use a coalesce statement like the following, but I can't get it to work because of the goofy syntax for coalesce. Is there a better way to accomplish this goal or am I just making a dumb syntactical error?
declare #StudentsID_CSVString varchar(128)
select Dept + Level as 'Class',
coalesce(#StudentsID_CSVString + ',', '')
+ CAST(StudentID as varchar(8)) as 'StudentsID_CSVString'
from Classes
left outer join StudentsInClasses ON Classes.ID = StudentsInClasses.ClassID
I used the following code to test:
declare #Students table (ID int, Name varchar(50), Gpa decimal(3,2))
declare #Classes table (ID int, Dept varchar(4), [Level] varchar(3))
declare #StudentsInClasses table (StudentID int, ClassID int)
declare #StudentsID_CSVString varchar(128)
insert into #Students (ID) values (1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12),(13),(14),(15),(16),(17),(18)
insert into #Classes (ID, Dept, [Level]) values (1, 'BA', '302'), (2, 'BA', '379'), (3, 'BA', '483')
insert into #StudentsInClasses (StudentID, ClassID) values
(1,1),(2,1),(3,1),(4,1),(5,1),(6,1),(7,1),(8,1),(9,1),
(4,2),(7,2),(9,2),(12,2),(15,2),
(7,3),(9,3),(12,3),(18,3)
With the technique to fill a variable #StudentsID_CSVString with a comma separated string you will only be able to get the string for one class at a time. To get more than one class in the result set you can use for xml path.
select C.Dept+C.Level as Class,
stuff((select ','+cast(S.StudentID as varchar(10))
from StudentsInClasses as S
where S.ClassID = C.ID
for xml path('')), 1, 1, '') as StudentsID_CSVString
from Classes as C
Test the query here: https://data.stackexchange.com/stackoverflow/q/115573/

Insert multiple rows WITHOUT repeating the "INSERT INTO ..." part of the statement?

I know I've done this before years ago, but I can't remember the syntax, and I can't find it anywhere due to pulling up tons of help docs and articles about "bulk imports".
Here's what I want to do, but the syntax is not exactly right... please, someone who has done this before, help me out :)
INSERT INTO dbo.MyTable (ID, Name)
VALUES (123, 'Timmy'),
(124, 'Jonny'),
(125, 'Sally')
I know that this is close to the right syntax. I might need the word "BULK" in there, or something, I can't remember. Any idea?
I need this for a SQL Server 2005 database. I've tried this code, to no avail:
DECLARE #blah TABLE
(
ID INT NOT NULL PRIMARY KEY,
Name VARCHAR(100) NOT NULL
)
INSERT INTO #blah (ID, Name)
VALUES (123, 'Timmy')
VALUES (124, 'Jonny')
VALUES (125, 'Sally')
SELECT * FROM #blah
I'm getting Incorrect syntax near the keyword 'VALUES'.
Your syntax almost works in SQL Server 2008 (but not in SQL Server 20051):
CREATE TABLE MyTable (id int, name char(10));
INSERT INTO MyTable (id, name) VALUES (1, 'Bob'), (2, 'Peter'), (3, 'Joe');
SELECT * FROM MyTable;
id | name
---+---------
1 | Bob
2 | Peter
3 | Joe
1 When the question was answered, it was not made evident that the question was referring to SQL Server 2005. I am leaving this answer here, since I believe it is still relevant.
INSERT INTO dbo.MyTable (ID, Name)
SELECT 123, 'Timmy'
UNION ALL
SELECT 124, 'Jonny'
UNION ALL
SELECT 125, 'Sally'
For SQL Server 2008, can do it in one VALUES clause exactly as per the statement in your question (you just need to add a comma to separate each values statement)...
If your data is already in your database you can do:
INSERT INTO MyTable(ID, Name)
SELECT ID, NAME FROM OtherTable
If you need to hard code the data then SQL 2008 and later versions let you do the following...
INSERT INTO MyTable (Name, ID)
VALUES ('First',1),
('Second',2),
('Third',3),
('Fourth',4),
('Fifth',5)
Using INSERT INTO ... VALUES syntax like in Daniel Vassallo's answer
there is one annoying limitation:
From MSDN
The maximum number of rows that can be constructed by inserting rows directly in the VALUES list is 1000
The easiest way to omit this limitation is to use derived table like:
INSERT INTO dbo.Mytable(ID, Name)
SELECT ID, Name
FROM (
VALUES (1, 'a'),
(2, 'b'),
--...
-- more than 1000 rows
)sub (ID, Name);
LiveDemo
This will work starting from SQL Server 2008+
This will achieve what you're asking about:
INSERT INTO table1 (ID, Name)
VALUES (123, 'Timmy'),
(124, 'Jonny'),
(125, 'Sally');
For future developers, you can also insert from another table:
INSERT INTO table1 (ID, Name)
SELECT
ID,
Name
FROM table2
Or even from multiple tables:
INSERT INTO table1 (column2, column3)
SELECT
t2.column,
t3.column
FROM table2 t2
INNER JOIN table3 t3
ON t2.ID = t3.ID
You could do this (ugly but it works):
INSERT INTO dbo.MyTable (ID, Name)
select * from
(
select 123, 'Timmy'
union all
select 124, 'Jonny'
union all
select 125, 'Sally'
...
) x
You can use a union:
INSERT INTO dbo.MyTable (ID, Name)
SELECT ID, Name FROM (
SELECT 123, 'Timmy'
UNION ALL
SELECT 124, 'Jonny'
UNION ALL
SELECT 125, 'Sally'
) AS X (ID, Name)
This looks OK for SQL Server 2008. For SS2005 & earlier, you need to repeat the VALUES statement.
INSERT INTO dbo.MyTable (ID, Name)
VALUES (123, 'Timmy')
VALUES (124, 'Jonny')
VALUES (125, 'Sally')
EDIT:: My bad. You have to repeat the 'INSERT INTO' for each row in SS2005.
INSERT INTO dbo.MyTable (ID, Name)
VALUES (123, 'Timmy')
INSERT INTO dbo.MyTable (ID, Name)
VALUES (124, 'Jonny')
INSERT INTO dbo.MyTable (ID, Name)
VALUES (125, 'Sally')
It would be easier to use XML in SQL Server to insert multiple rows otherwise it becomes very tedious.
View full article with code explanations here http://www.cyberminds.co.uk/blog/articles/how-to-insert-multiple-rows-in-sql-server.aspx
Copy the following code into sql server to view a sample.
declare #test nvarchar(max)
set #test = '<topic><dialog id="1" answerId="41">
<comment>comment 1</comment>
</dialog>
<dialog id="2" answerId="42" >
<comment>comment 2</comment>
</dialog>
<dialog id="3" answerId="43" >
<comment>comment 3</comment>
</dialog>
</topic>'
declare #testxml xml
set #testxml = cast(#test as xml)
declare #answerTemp Table(dialogid int, answerid int, comment varchar(1000))
insert #answerTemp
SELECT ParamValues.ID.value('#id','int') ,
ParamValues.ID.value('#answerId','int') ,
ParamValues.ID.value('(comment)[1]','VARCHAR(1000)')
FROM #testxml.nodes('topic/dialog') as ParamValues(ID)
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
OR YOU CAN USE ANOTHER WAY
INSERT INTO MyTable (FirstCol, SecondCol)
VALUES
('First',1),
('Second',2),
('Third',3),
('Fourth',4),
('Fifth',5)
I've been using the following:
INSERT INTO [TableName] (ID, Name)
values (NEWID(), NEWID())
GO 10
It will add ten rows with unique GUIDs for ID and Name.
Note: do not end the last line (GO 10) with ';' because it will throw error: A fatal scripting error occurred. Incorrect syntax was encountered while parsing GO.
Corresponding to INSERT (Transact-SQL) (SQL Server 2005) you can't omit INSERT INTO dbo.Blah and have to specify it every time or use another syntax/approach,
In PostgreSQL, you can do it as follows;
A generic example for a 2 column table;
INSERT INTO <table_name_here>
(<column_1>, <column_2>)
VALUES
(<column_1_value>, <column_2_value>),
(<column_1_value>, <column_2_value>),
(<column_1_value>, <column_2_value>),
...
(<column_1_value>, <column_2_value>);
See the real world example here;
A - Create the table
CREATE TABLE Worker
(
id serial primary key,
code varchar(256) null,
message text null
);
B - Insert bulk values
INSERT INTO Worker
(code, message)
VALUES
('a1', 'this is the first message'),
('a2', 'this is the second message'),
('a3', 'this is the third message'),
('a4', 'this is the fourth message'),
('a5', 'this is the fifth message'),
('a6', 'this is the sixth message');
This is working very fast,and efficient in SQL.
Suppose you have Table Sample with 4 column a,b,c,d where a,b,d are int and c column is Varchar(50).
CREATE TABLE [dbo].[Sample](
[a] [int] NULL,
[b] [int] NULL,
[c] [varchar](50) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
[D] [int] NULL
)
So you cant inset multiple records in this table using following query without repeating insert statement,
DECLARE #LIST VARCHAR(MAX)
SET #LIST='SELECT 1, 1, ''Charan Ghate'',11
SELECT 2,2, ''Mahesh More'',12
SELECT 3,3,''Mahesh Nikam'',13
SELECT 4,4, ''Jay Kadam'',14'
INSERT SAMPLE (a, b, c,d) EXEC(#LIST)
Also With C# using SqlBulkCopy bulkcopy = new SqlBulkCopy(con)
You can insert 10 rows at a time
DataTable dt = new DataTable();
dt.Columns.Add("a");
dt.Columns.Add("b");
dt.Columns.Add("c");
dt.Columns.Add("d");
for (int i = 0; i < 10; i++)
{
DataRow dr = dt.NewRow();
dr["a"] = 1;
dr["b"] = 2;
dr["c"] = "Charan";
dr["d"] = 4;
dt.Rows.Add(dr);
}
SqlConnection con = new SqlConnection("Connection String");
using (SqlBulkCopy bulkcopy = new SqlBulkCopy(con))
{
con.Open();
bulkcopy.DestinationTableName = "Sample";
bulkcopy.WriteToServer(dt);
con.Close();
}
Others here have suggested a couple multi-record syntaxes. Expounding upon that, I suggest you insert into a temp table first, and insert your main table from there.
The reason for this is loading the data from a query can take longer, and you may end up locking the table or pages longer than is necessary, which slows down other queries running against that table.
-- Make a temp table with the needed columns
select top 0 *
into #temp
from MyTable (nolock)
-- load data into it at your leisure (nobody else is waiting for this table or these pages)
insert #temp (ID, Name)
values (123, 'Timmy'),
(124, 'Jonny'),
(125, 'Sally')
-- Now that all the data is in SQL, copy it over to the real table. This runs much faster in most cases.
insert MyTable (ID, Name)
select ID, Name
from #temp
-- cleanup
drop table #temp
Also, your IDs should probably be identity(1,1) and you probably shouldn't be inserting them, in the vast majority of circumstances. Let SQL decide that stuff for you.
Oracle SQL Server Insert Multiple Rows
In a multitable insert, you insert computed rows derived from the rows returned from the evaluation of a subquery into one or more tables.
Unconditional INSERT ALL:- To add multiple rows to a table at once, you use the following form of the INSERT statement:
INSERT ALL
INTO table_name (column_list) VALUES (value_list_1)
INTO table_name (column_list) VALUES (value_list_2)
INTO table_name (column_list) VALUES (value_list_3)
...
INTO table_name (column_list) VALUES (value_list_n)
SELECT 1 FROM DUAL; -- SubQuery
Specify ALL followed by multiple insert_into_clauses to perform an unconditional multitable insert. Oracle Database executes each insert_into_clause once for each row returned by the subquery.
MySQL Server Insert Multiple Rows
INSERT INTO table_name (column_list)
VALUES
(value_list_1),
(value_list_2),
...
(value_list_n);
Single Row insert Query
INSERT INTO table_name (col1,col2) VALUES(val1,val2);
Created a table to insert multiple records at the same.
CREATE TABLE TEST
(
id numeric(10,0),
name varchar(40)
)
After that created a stored procedure to insert multiple records.
CREATE PROCEDURE AddMultiple
(
#category varchar(2500)
)
as
BEGIN
declare #categoryXML xml;
set #categoryXML = cast(#category as xml);
INSERT INTO TEST(id, name)
SELECT
x.v.value('#user','VARCHAR(50)'),
x.v.value('.','VARCHAR(50)')
FROM #categoryXML.nodes('/categories/category') x(v)
END
GO
Executed the procedure
EXEC AddMultiple #category = '<categories>
<category user="13284">1</category>
<category user="132">2</category>
</categories>';
Then checked by query the table.
select * from TEST;

How to select data from two sql tables with special field layout

I have two tables as shown in the example. Now I want to select the data in the format presented in the comment lines below.
create table cust (nbr varchar(8))
create table data (nbr varchar(8),fld varchar(8),val varchar(8))
insert into cust (nbr) values ('AA')
insert into data (nbr,fld,val) values ('AA','1','one')
insert into data (nbr,fld,val) values ('AA','2','two')
insert into data (nbr,fld,val) values ('AA','3','three')
insert into data (nbr,fld,val) values ('AA','1','uno')
insert into data (nbr,fld,val) values ('AA','2','dos')
insert into data (nbr,fld,val) values ('AA','3','tres')
select * from cust
select * from data
drop table cust
drop table data
-- AA, One, Two, Three
-- AA, Uno, Dos, Tres
Any ideas how to join these tables to get the desired output.
At the moment you don't have anything in the data table which would allow you to group the rows into anything you can work with. If you add a languageID column to the data table as suggested by #avery_larry, you could write your select as follows:
create table cust (nbr varchar(8))
create table data (nbr varchar(8),fld varchar(8),val varchar(8), languageID int)
insert into cust (nbr) values ('AA')
insert into data (nbr,fld,val, languageID) values ('AA','1','one', 1)
insert into data (nbr,fld,val, languageID) values ('AA','2','two', 1)
insert into data (nbr,fld,val, languageID) values ('AA','3','three', 1)
insert into data (nbr,fld,val, languageID) values ('AA','1','uno', 2)
insert into data (nbr,fld,val, languageID) values ('AA','2','dos', 2)
insert into data (nbr,fld,val, languageID) values ('AA','3','tres', 2);
select * from cust;
select * from data;
with data_cte(nbr, languageID)
as
(
select nbr, languageID
from data with (nolock)
group by nbr, languageID
)
select c.nbr,
SUBSTRING(
(
SELECT ','+d.val AS [text()]
FROM [data] d
WHERE d.nbr = dc.nbr and d.languageID = dc.languageID
ORDER BY d.fld
FOR XML PATH ('')
), 2, 1000) as 'Values'
from data_cte dc
inner join cust c on c.nbr = dc.nbr;
drop table cust
drop table data
The sql for concatenating the strings was taken from this post How to concatenate text from multiple rows into a single text string in SQL server?, with a slight tweek to add the with.
Hope this helps.

Resources