I have a table with some duplicates Data, I want to merge the job and unit field in one field.
My data currently looks like this:
name job1 Unit1_Level1 Unit1__Level2
-------------------------------------------------------
A Teacher Infomation Information_office
A Staff Secretary Secretary_office
A Staff Engineer Engineer_office
B Teacher Finacial Finacial_office
C Teacher Engineer Engineer_office
C Staff Library Library_office
D Staff Library Library_office
And I want it to look like this:
name job
----------------------------------------------------------------------------------------------------------------
A Teacher-Infomation-Information_office:Staff-Secretary-Secretary_office:Staff-Engineer-Engineer_office
B Teacher-Finacial-Finacial_office
C Teacher-Engineer-Engineer_office:Staff-Library-Library_office
D Staff-Library-Library_office
I try the FOR XML PATH , but still have the duplicate row.
Please help me about the question, thank you.
In TSQL you could use STRING_AGG()
select STRING_AGG(concat(job1,'-', Unit1_level1, '-', Unit1_level2, ':'))
from your_table
Try this combination STRING_AGG an CONCAT_WS
SELECT name, STRING_AGG(a.ct, ':' )
FROM (
SELECT name, CONCAT_WS('-', job1, Unit1_level1, Unit1__Level2, ':') ct
FROM MyTable
) a
GROUP BY name
You could use STRING_AGG() and CONCAT_WS() as the following:
SELECT Name,
STRING_AGG(CONCAT_WS('-', job1, Unit1_Level1, Unit1__Level2), ':')
FROM YourTable
GROUP BY Name;
String_Agg requires an separator value
select STRING_AGG(concat(job1,'-', Unit1_Level1, '-', Unit1_Level2, ':'),' ') a from #tmp
Related
I have a table of similar structure:
Name Movies_Watched
A Terminator
B Alien
A Batman
B Rambo
B Die Hard
....
I am trying to get this:
Name Movies_Watched
A Terminator;Batman
B Alien, Die Hard, Rambo
My initial guess was:
SELECT Name, Movies_Watched || Movies_Watched from TABLE
But obviously that's wrong. Can someone tell me how can I loop through the 2nd column and concatenate them? What's the logic like?
Got to know that group_concat is the right approach. But haven't been able to figure it out yet. When I've tried:
select name, group_concat(movies_watched) from table group by 1
But it throws an error saying User-defined transform function group_concat must have an over clause
You are looking for string_agg():
select name, string_agg(movie_watched, ';') as movies_watched
from t
group by name;
That said, you are using Postgres, so you should learn how to use arrays instead of strings for such things. For instance, there is no confusion with arrays when the movie name has a semicolon. That would be:
select name, array_agg(movie_watched) as movies_watched
from t
group by name;
use array_agg
SELECT Name, array_agg(Movies_Watched)
FROM data_table
GROUP BY Name
i think you need listagg or group_concat as you are using vertica upper is postgrey solution
SELECT Name, listagg(Movies_Watched)
FROM data_table
GROUP BY Name
or
select Name,
group_concat(Movies_Watched) over (partition by Name order by name) ag
from mytable
As already mentioned, in Vertica it's LISTAGG():
WITH
input(nm,movies_watched) AS (
SELECT 'A','Terminator'
UNION ALL SELECT 'B','Alien'
UNION ALL SELECT 'A','Batman'
UNION ALL SELECT 'B','Rambo'
UNION ALL SELECT 'B','Die Hard'
)
SELECT
nm AS "Name"
, LISTAGG(movies_watched) AS movies_watched
FROM input
GROUP BY nm;
-- out Name | movies_watched
-- out ------+----------------------
-- out A | Terminator,Batman
-- out B | Alien,Rambo,Die Hard
-- out (2 rows)
-- out
-- out Time: First fetch (2 rows): 12.735 ms. All rows formatted: 12.776 ms
Sql Query for select the value before and after the comma
There is a table called employee, there is 3 fields..Id,Name,Departmentid
In DepartmentId it consist of 3 id 201,301,401.
From this want to execute
Select * from employee where DepartmentId =301
This will work if you're always looking to match on the number in the middle, e.g. 301 in your example.
SELECT *
FROM employee
WHERE
SUBSTRING (
DepartmentId,
CHARINDEX(',', DepartmentId, 0) + 1,
CHARINDEX(',', DepartmentId, CHARINDEX(',', DepartmentId, 0) + 1) - CHARINDEX(',', DepartmentId, 0) - 1
) = '301'
If 301 can be in any location in that field then you can just use LIKE
SELECT *
FROM employee
WHERE DepartmentId LIKE '%301%'
SELECT * FROM EMPLOYEE WHERE DEPARTMENTID LIKE '%301%'
Even if it works, I don't really understand how your DB is designed and why you need commas
You can use parsename() function:
. . .
where parsename(replace(DepartmentId, ',', '.'), 2) = 301;
However like predicate also useful:
. . .
where DepartmentId LIKE '%301%';
Comma seperated columns are an antipattern. You want to normalize your departments.
However, here's one way to do it:
select * from employee
where '301' in (SELECT value FROM STRING_SPLIT(department, ','))
If you have departments like 1301 or 3011 then a simple LIKE might fail
Please check following SQL code
select * from employees where ','+departmentid+',' like '%,301,%'
And other option is to split departmentid column into each seperate department id value list
If you have a SQL Server before SQL Server 2016 then you need your own custome SQL string split function.
Then you can use following SQL query
select e.*
from employees as e
cross apply dbo.split(departmentid,',') as s
where s.val = '301'
If you are working on SQL Server 2016 or SQL Server 2017, string_split build-in SQL function can be used as follows again in a CROSS APPLY query
SELECT e.*
FROM employees as e
CROSS APPLY STRING_SPLIT(departmentid, ',')
WHERE value = '301'
One last method can be using SQL XML query by modifying the comma seperated list into an XML data as follows
select
Id, Name, sqlXML.value('.','varchar(5)') as DepId
from (
SELECT
Id, Name,
convert(xml, '<root><t>' + REPLACE(Departmentid, ',', '</t><t>') + '</t></root>') as dlist
FROM employees
) tbl
CROSS APPLY dlist.nodes('/root/t') as XMLData(sqlXML)
WHERE sqlXML.value('.','varchar(5)') = '301'
I have a table with unique names and a combination of those same names separated by commas in the same field as below:
Bill
Mark
Steve
Bill, Mark
Mark, Steve
Bill,Mark, Steve
I would like to Group the names not separated by a comma for a count of those where the name exists such as:
Bill 3
Mark 4
Steve 3
In the future someone may add another name to the table so I can't use a Case statement with static names. I would like something like this:
SELECT
Name
FROM
My_Table
Group By
Name Like (SELECT Name FROM My_Table Where Name Not Like '%,%')
Is that possible?
Select N.Name, COUNT(*)
FROM (
SELECT Name
FROM My_Table
WHERE Name NOT LIKE '%,%'
) Names N
JOIN My_Table MT
ON (MT.Name LIKE '%' + N.Name + ',%' OR MT.Name LIKE '%,' + N.Name + '%' OR MT.Name = N.Name)
GROUP BY N.Name
I don't have an instance of SQLServer to test this out, but an approach that may work is selecting only the simple-name records and then a nested SELECT expression that will count all records with that name. Something like this:
SELECT
Name,
(SELECT COUNT(*) FROM YourTable Y2 WHERE Name LIKE ('%' + Y1.Name + '%'))
FROM YourTable Y1 WHERE NAME NOT LIKE '%,%'
It will fail, of course, on nested names. (Bob and Bobby, if they're different people, for instance). A more robust approach would require removing all the spaces from around commas and building out the LIKE expression into three LIKEs ORed together. If you can't create a LIKE value in-line the way I did, you can substitute whatever the SQLServer function for location-within-string is.
But, honestly, I'd project a temporary normalized table and base your report off that.
I'm trying to generate a SQL query that can join two tables together and return the result .. but the second table is 'flattened'. I'm not sure if that's the correct technical term. Is it denormalized?
Anyways, can someone suggest how I could do this?
Table: Users
UserId Name
1 Pure.Krome
2 John
3 Jill
4 Jane
Table: UserAliases
UserAliasId UserId Alias
1 1 Idiot
2 1 PewPew
3 3 BlahBlahBlah
Desired results
UserId Name Aliases
1 Pure.Krome Idiot PewPew
2 John
3 Jill BlahBlahBlah
4 Jane
Please note:
A user does NOT need to have an alias. So that's a zero->many relationship (outer join)
The delimiter for the flattening of the 2nd table is a SPACE. If an alias has a space, bad luck for me. (Consider it, bad data).
Another example of my problem is to think of a StackOverflow question + tags.
http://groupconcat.codeplex.com/ has a clone of MySQL's GROUP_CONCAT() implemented as a CLR aggregation function. I guess the SQL is not the problem, but I might as well:
SELECT
[Users].[UserId] AS UserId,
[Users].[Name] AS Name,
GROUP_CONCAT_D([UserAliases].[Alias]," ") AS Aliases
FROM [Users]
OUTER JOIN [UserAliases] ON [Users].[UserId]=[UserAliases].[UserId]
or similar.
This is not tested but give it a try. I have no server here.
SELECT a.UserID,
a.[Name],
coalesce(NewTable.NameValues, '') Aliases
FROM Users a LEFT JOIN
(
SELECT UserID,
STUFF((
SELECT ' ' + [Name]
FROM UserAliases
WHERE ( UserID = Results.UserID )
FOR XML PATH('')), 1, 2, '') AS NameValues
FROM UserAliases Results
GROUP BY UserID
) NewTable
on a.UserID = NewTable.UserID
Here's SQL Fiddle Output
FOR XML PATH is handy in this situation:
SELECT UserID, Name
, LTRIM(RTRIM((SELECT ' ' + Alias
FROM UserAliases WHERE UserID = u.UserID
FOR XML PATH('')))) AS Aliases
FROM Users u
I have two tables to store student data for a grade-school classroom:
Behavior_Log has the columns student_id, comments, date
Student_Roster has the columns student_id, firstname, lastname
The database is used to store daily comments about student behavior, and sometimes the teacher makes multiple comments about a student in a given day.
Now let's say the teacher wants to be able to pull up a list of the last 3 comments made for EACH student, like this:
Jessica 7/1/09 talking
Jessica 7/1/09 passing notes
Jessica 5/3/09 absent
Ciboney 7/2/09 great participation
Ciboney 4/30/09 absent
Ciboney 2/22/09 great participation
...and so on for the whole class
The single SQL query must return a set of comments for each student to eliminate the human-time-intensive need for the teacher to run separate queries for each student in the class.
I know that this sounds similar to
SQL Statement Help - Select latest Order for each Customer but I need to display the last 3 entries for each person, I can't figure out how to get from here to there.
Thanks for your suggestions!
A slightly modified solution from this article in my blog:
Analytic functions: SUM, AVG, ROW_NUMBER
SELECT student_id, date, comment
FROM (
SELECT student_id, date, comment, (#r := #r + 1) AS rn
FROM (
SELECT #_student_id:= -1
) vars,
(
SELECT *
FROM
behavior_log a
ORDER BY
student_id, date DESC
) ao
WHERE CASE WHEN #_student_id <> student_id THEN #r := 0 ELSE 0 END IS NOT NULL
AND (#_student_id := student_id) IS NOT NULL
) sc
JOIN Student_Roster sr
ON sr.student_id = sc.student_id
WHERE rn <= 3
A different approach would be to use the group_concat function and a single sub select and a limit on that subselect.
select (
select group_concat( concat( student, ', ', date,', ', comment ) separator '\n' )
from Behavior_Log
where student_id = s.student_id
group by student_id
limit 3 )
from Student_Roster s