Lets say I have a table in SQLServer named Tasks. It contains some tasks with people assigned to tasks. Workers are in one column. They are separated by semicolon.
ID Workers
1 John Newman; Troy Batchelor; Mike Smith
2 Chris Portman
3 Sara Oldman; Greg House
I need to separate workers from column like below
The result:
ID Worker
1 John Newman
1 Troy Batchelor
1 Mike Smith
2 Chris Portman
3 Sara Oldman
3 Greg House
I have no idea what to do.
Do I have to use some procedure or simple query is enough?
i solved your problem without using any function or stored procedure
SELECT ID,Workers FROM tblSemiColon
SELECT ID,
LTRIM(RTRIM(m.n.value('.[1]','varchar(8000)'))) AS Workers
FROM
(
SELECT ID,CAST('<XMLRoot><RowData>' + REPLACE(Workers,';','</RowData><RowData>') + '</RowData></XMLRoot>' AS XML) AS x
FROM tblSemiColon
)t
CROSS APPLY x.nodes('/XMLRoot/RowData')m(n)
this will work across all the version of sql server..i had tested it....
you can reduce the size of varchar length..
If you are on sql-server 2016 or higher you can use STRING_SPLIT function
SELECT id, value
FROM Tasks
CROSS APPLY STRING_SPLIT(Workers, ';')
Related
EDIT:
I've since edited the query using JOINS instead of a WHERE clause in light
of suggested comments. I was using a WHERE clause instead of JOIN because I
couldn't get it to work across three tables but have figured it out. I've
also inserted SELECT DISTINCT because it does solve the problem.
Thanks #MichaelEvanchik, and #SeanLange for the help. Im still learning and hope I don't frustrate you guys too much.
I've looked over many of the multiple return threads and don't seem to find an answer that helps me.
I have 4 tables.
table1
ID Cat1_Name1 Cat1_Name2 Cat2_Name1 Cat2_Name2
12 Mike Mike George Mike
13 Jen Jen Amy Amy
14 Jeff Jen Mike Ben
15 Jeff Jeff Fred Tom
16 George Jen Luke Amy
table2
ID Cat1_Name1 Cat1_Name2 Cat2_Name1 Cat2_Name2
25 Mike Mike Jen George
table3
Name Cat1_Value Cat2_Value
Mike 6.5 20.25
Jen 10.2 0.5
Jeff 11.5 1.5
George 8.0 27.1
table4
Name Cat1_Value Cat2_Value
Mike 7.8 20.0
Jen 6.0 13.0
Jeff 13.2 5.0
George 8.0 1.2
Before anyone asks, the set of names in table2 must stay separate from table1. It isn’t duplicate information, but a SINGLE UNKNOWN SET that will be compared to every record in table1, which can contain millions of known sets (i.e.; no ID’s in table1 will ever match the ID in table2). If you look at the tables you can see that the set of names CAN match between table1 and table2 but do not have to. For example, the names for cat1 match between tables 1 and 2 for ID 12 and 25 (all 4 are Mike) but doesn't match any between IDs 13,14,15,16 and 25 (only the two in 25 are Mike). While at cat2, ID 12 and 25 match partially (i.e., the names in cat2 between tables 1 and 2 contain the name George but do not match in the second name). Here I show two categories. There will be upwards of 30 categories of names for one record but for now, I am focusing on 1 to solve this particular problem. Cat1_Name1, Cat1_Name2. I will worry about aggregating the different categories and logical name combinations with JOINs and UNIONs and answer my other question later….hopefully.
I want to create a new table that returns the ID from table1, with the associated value for each category depending on how many names match in the category. For example, since cat1_name 1 and 2 in table1 are mike,mike AND cat1_name 1 and 2 in table2 are mike,mike, return the ID from table 1 (12) and value in table 3 for cat1 (6.5). Different sets of matching names would return values from different tables (i.e.; the partially matching set in cat2 between 12 and 25 might return the value from table4 etc). I asked a similar question about this previously, here but the problem was different:
Returning Results from different tables depending on conditions from two other tables
I have a partial answer for it but now have a different problem. I plan on posting an answer to the first, once I figure out this problem (hopefully with a little help ).
Here’s my query:
SELECT DISTINCT dbo.table1.ID, dbo.table3.Cat1_Value
INTO Cat1Table
FROM dbo.table2
INNER JOIN dbo.table3 ON (dbo.table1.Cat1_Name1 = dbo.table3.Name ) AND
(dbo.table1.Cat1_Name2 = dbo.table3.Name )
INNER JOIN dbo.table1 ON (dbo.table2.Cat2_Name1 = dbo.table3.Name ) AND
(dbo.table2.Cat2_Name2 = dbo.table3.Name )
Result table that I want:
Cat1Table
ID Cat1_Value
12 6.5
What I’m getting:
Cat1Table
ID Cat1_Value
12 6.5
12 6.5
Why am I getting a duplicate? Is it my logic or am I missing something else more simple? If I use SELECT DISTINCT it gives me the correct return but I'm thinking there might be a more efficient way because this will be expanded to millions of records. Wouldn't SELECT DISTINCT slow everything down?
I have 2 tables on SQL Server 2008, each one has a single column and the same rows count number:
USERS OPERATION
Name Operation
----------- -----------
John W383
William R823
Karen X933
Peter M954
Alex S744
I need to perform every week a random draw between the 2 tables to get something like the follow and save it into a 3rd. table:
DRAW_RESULT:
Name Operation_Assigned Week_Number
----------------------------------------------
Peter M954 2
William W383 2
John S744 2
Alex X933 2
Karen R823 2
Name Operation_Assigned Week_Number
----------------------------------------------
William R823 3
Alex M954 3
Karen X933 3
John S744 3
Peter W383 3
How can I do this using T-SQL?
If I understood correctly what you're doing, something like this should work:
select name, operation from (
select
row_number() over (order by (select null)) as RN,
name
from
users
) U join (
select
row_number() over (order by newid()) as RN,
operation
from
operation
) O on U.RN = O.RN
Edit: row_number with newid() works, so removed the extra derived table.
Here's also SQL Fiddle to test this.
I've been approaching a problem perhaps in the wrong way. I've researched pivot examples
http://www.codeproject.com/Tips/500811/Simple-Way-To-Use-Pivot-In-SQL-Query
How to create a pivot query in sql server without aggregate function
but they aren't the type I'm looking for.. or perhaps I'm approaching this in the wrong way, and I'm new to sql server.
I want to transform:
Student:
studid | firstname | lastname | school
-----------------------------------------
1 mike lee harvard
1 mike lee ucdavis
1 mike lee sfsu
2 peter pan chico
2 peter pan ulloa
3 peter smith ucb
Desired output: (note for school, want only 3 columns max.)
studid| firstname | lastname | school1 | school2 | school3
---------------------------------------------------------------------
1 mike lee Harvard ucdavis sfsu
2 peter pan chico ulloa
3 peter smith ucb
The tutorials I see shows the use of Sum() , count() ... but I have no idea how to pivot string values of one column and put them into three columns.
You can get the results you desire by taking max(school) for each pivot value. I'm guessing the pivot value you want is rank over school partitioned by student. This would be the query for that:
select * from
(select *, rank() over (partition by studid order by school) rank from student) r
pivot (max(school) for rank in ([1],[2],[3])) pv
note that max doesn't actually do anything. the query would return the same results if you replaced it with min. just the pivot syntax requires the use of an aggregate function here.
I've been reading other similar "multiple records into one" posts, but either cannot seem to get any to work, or they don't really apply to what I am trying to do.
Here are my 3 tables. vehicle, vehicle_repair, comments
vehicle columns: vehicle_name and other vehicle related info,vehicle_make, vehicle_model
vehicle_repair columns: vehicle_name, vehicle_repair_type, vehicle_repair_num, etc, etc
comments columns: vehicle_name,vehicle_repair_num, comments_detail
The way the program is written, if I write more than 1 line of comments, it doesn't concatenate them, it makes 1 entry for each line, ie:
comments table:
vehicle_name vehicle_rpr_num comments_detail
--------------------------------------------------------------------
150 1 replaced hose
750 1 replaced belt
750 2 replaced fuel and also saw that the
750 2 timing belt needs to be replaced
750 2 as well
I was trying to use something like:
select
substring((select ' '+comments_detail AS 'data()'
from comments
for xml path('')), 3, 80) as 'comments_detail'
from
comments
I tried to add the join and other tables inside the substring but then the comments_details become all jacked up, like it then combines 20 comments together instead of 1 at a time.
I'd rather start from scratch and see if I can get it working another way.
My issue comes in when I try to link the 3 tables above.
I do not know how to put in the other fields that I need from the vehicle table, ie vehicle_make, vehicle_model
Any ideas? Am I writing my concatenate completely wrong? I am trying to put this into a stored procedure, would a view be better?
SELECT c1.vehicle_name,
c1.vehicle_rpr_num,
STUFF((SELECT ' ' + comments_detail
FROM comments c2
WHERE c2.vehicle_name = c1.vehicle_name
AND c2.vehicle_rpr_num = c1.vehicle_rpr_num
FOR XML PATH(''), TYPE).value('.', 'varchar(max)'),
1,1,'')
AS comments
FROM comments c1
GROUP BY c1.vehicle_name, c1.vehicle_rpr_num;
SQLFiddle with the sample comments data.
You were on the right track using FOR XML PATH to concatenate the comments. There are many different ways to concatenate, a good article on the pros/cons of each is here. I'd put this into a view definition to allow for easier joining with other tables.
CREATE TABLE tbl (vehicle_name INT,vehicle_rpr_num INT,comments_detail NVARCHAR(1000))
INSERT INTO tbl
VALUES
(150,1,'replaced hose'),
(750,1,'replaced belt'),
(750,2,'replaced fuel and also saw that the'),
(750,2,'timing belt needs to be replaced'),
(750,2,'as well')
SELECT DISTINCT t.vehicle_name, t.vehicle_rpr_num , STUFF(List.Comments, 1 ,2, '') AS Comments
FROM tbl t
CROSS APPLY (
SELECT ' ' + comments_detail [text()]
FROM tbl
WHERE vehicle_name = t.vehicle_name
AND vehicle_rpr_num = t.vehicle_rpr_num
FOR XML PATH('')
)List(Comments)
Result Set
vehicle_name vehicle_rpr_num Comments
150 1 eplaced hose
750 1 eplaced belt
750 2 eplaced fuel and also saw that the timing belt needs to be replaced as well
I'm hoping to return a single row with a comma separated list of values from a query that returns multiple rows in Oracle, essentially flattening the returned rows into a single row.
In PostgreSQL this can be achieved using the array and array_to_string functions like this:
Given the table "people":
id | name
---------
1 | bob
2 | alice
3 | jon
The SQL:
select array_to_string(array(select name from people), ',') as names;
Will return:
names
-------------
bob,alice,jon
How would I achieve the same result in Oracle 9i?
Thanks,
Matt
Tim Hall has the definitive collection of string aggregation techniques in Oracle.
If you're stuck on 9i, my personal preference would be to define a custom aggregate (there is an implementation of string_agg on that page) such that you would have
SELECT string_agg( name )
FROM people
But you have to define a new STRING_AGG function. If you need to avoid creating new objects, there are other approaches but in 9i they're going to be messier than the PostgreSQL syntax.
In 10g I definitely prefer the COLLECT option mentioned at the end of Tim's article.
The nice thing about that approach is that the same underlying function (that accepts the collection as an argument), can be used both as an aggregate and as a multiset function:
SELECT deptno, tab_to_string(CAST(MULTISET(SELECT ename FROM emp
WHERE deptno = dept.deptno) AS t_varchar2_tab), ',') FROM dept
However in 9i that's not available. SYS_CONNECT_BY_PATH is nice because it's flexible, but it can be slow, so be careful of that.