There are 2 tables and I must do an inner join.
First table called People
Name, Surname, id, and value
Second table called Work
id (external key of fist table), category, state, roles, date.
Column "Roles" can have multiple values (employee director workers etc).
I must show with inner join the history of roles for each people in one row ( Name Surname roles1, roles 2 roles3)
Example Jack Brown employee director workers
How can I show in one row multiple values contained in one Columns?
If you just need to see the roles but don't really require them to be in separate columns you can use listagg()
select p.id,
p.name,
p.surname,
listagg(w.roles, ',') within group (order by start_date) as all_rows
from people p
join work w on p.id = w.id
group by p.id, p.name, p.surname
This would output something like this:
ID | NAME | SURNAME | ALL_ROLES
---+--------+---------+-------------------------
1 | Jack | Brown | employee,worker,director
You can't actually have each role in a separate column, because in SQL the number of columns in a result is fixed. So you can't have a result that has three columns for the roles of "Jack Brown" and two column for the roles of "Arthur Dent".
You could write PL-SQL function which will select all related records from table Work by given id from table People and iterate it in a cursor to build a string with all of roles or you could generate XML by using of DBMS_XMLGEN.GETXML function if you are using the 10g version of Oracle or higher
Related
I have two tables in a SQL Server Database:
Table: People
Columns: ID, FirstName, LastName
Table: StandardNames
Columns: Nickname, StandardName
Sample Nicknames would be Rick, Rich, Richie when StandardName is Richard.
I would like to find duplicate contacts in my People table but replace any of the nicknames with the standard name. IE: sometimes I have Rich Smith other times it is Richard Smith in the People table. Is this possible? I realize it might be multiple joins to the same table but can't figure out how to start.
Firstly, you need to determine how many duplicates you have in your People table...
SELECT p.FirstName, COUNT(*)
FROM People AS p
INNER JOIN StandardNames AS sn
ON CHARINDEX(sn.Nickname, p.FirstName) > 0 OR
CHARINDEX(sn.Nickname, p.LastName) > 0
GROUP BY p.FirstName
HAVING COUNT(*) > 1
That's just to get an idea of what data you're trying to find in relation to the Nicknames that may possibly exist inside (as a wildcard word search) the Firstname and Lastname columns.
If you are happy with the items found then expand on the query to update the values.
Let's say you wanted to change the Firstname to be the Standardname...
UPDATE p2
SET p2.FirstName = p2.Standardname
FROM
(SELECT p.ID, sn.StandardName
FROM People AS p
INNER JOIN StandardNames AS sn
ON CHARINDEX(sn.Nickname, p.FirstName) > 0 OR
CHARINDEX(sn.Nickname, p.LastName) > 0) AS a
INNER JOIN People AS p2 ON p2.ID = a.ID
So this will obviously find all the People IDs that have a match based on the query above, and it will update the People table by replacing the FirstName with the StandardName.
However, there are issues with this due to the limitation of your question.
the StandardNames table should have its own ID field. All tables should have an ID column as its primary table. That's just my view.
this is only going to work for data it matches using the CHARINDEX() function. What you really need is something to find based on a "sound" or similarity to the nicknames. Check out the SOUNDEX() function and apply your logic from there.
And this is assuming your IDs above are unique!
Good luck
You could standardize the names by joining, and count the number of occurrences. Extracting the ID is a bit fiddly, but also quite possible. I'd suggest the following - use a case expression to find the contact with the standard name, and if you don't have one, just take the id of the first duplicate:
SELECT COALESCE(MIN(CASE FirstName WHEN StandardName THEN id END), MIN(id)),
StandardName,
LastName,
COUNT(*)
FROM People p
LEFT JOIN StandardNames s ON FirstName = Nickname AND
GROUP BY StandardName, LastName
Currently I have a staff table with columns:
Staff_Id, first_name, Surname.
My second table is:
Id, management_role.
When I link the tables each staff member gets added to every management role. So for example a person in first table called Jim is added three times as manager, supervisor, intern and this happens for every staff.
Some things to consider that are your ID columns are primary keys for their respective tables. If not are every value in the column is unique? Also are ids not
From your description you might be using a cross join here. The thing you need is inner join so it joins the matching id's together.
So you can do
SELECT *
FROM staff_table as st
INNER JOIN management_table as mt
ON st.Staff_Id = mt.ID
Problem Statement
I would like to select all of the resources that belong to a certain company, for each resource I would like to get a comma separated list of the groups that it belongs to (using string_agg).
DB Relationships
Resources
=============================
ResourceId (PK)
CompanyId (FKs to Company)
ResourceName
...
Other columns that are not important
Company
=============================
CompanyId (PK)
...
Other columns that are not important
GroupResources
=============================
GroupId (PK and FKs to Group)
ResourceId (PK and FKs to Resources)
Group
=============================
GroupId (PK)
CompanyId (FKs to Company)
GroupName
...
Other columns that are not important
What I currently have
I currently have two separate queries, Query A which retrieves all of the information I require except for the aggregate of the groups for each resource, and Query B which retrieves only the aggregate of the groups for each resource in the company.
Query A
SELECT resources.resourcename, ... other columns that I am selecting out
FROM resources
...
JOINs to tables to get date for some of the other columns
...
WHERE resources.companyid = 1234
ORDER BY resourcename ASC;
Query B
SELECT string_agg(groupname, ',') AS groups
FROM group
JOIN groupresources USING (groupid)
JOIN resources USING (resourceid)
WHERE resources.companyid = 1234
GROUP BY (resourcename)
ORDER BY groups ASC;
I know about using subqueries but in this case I'm not quite sure about how to apply that knowledge because it seems like I am trying to perform what would be a two part operation (in languages such as C#) in a single hit:
Get all of the resources for a company
Populate the groups property for each resource
Simply adding the string_agg statement to Query A will return an aggregated string for all groups in the company, NOT just the ones the resource is part of. This was an incorrect assumption that I was making, see answer for more details.
Desired Output
resource1, 'group1,group 2', ...other columns
resource2, 'group3', ...other columns
resource3, 'group1, group3', ...other columns
My assumption that adding string_agg to Query A would return a comma separated list of values for all of the groups in a company, rather than just the groups that the resource in the row belonged to was in correct.
In actual fact adding string_agg to Query A forced the use of the GROUP BY command because I had other columns that I was selecting outside of the aggregrate function (...column must appear in the GROUP BY clause or be used in an aggregate function...). Once I added the relevant columns (everything except the aggregated column) to the GROUP BY clause, my query functioned as expected.
Penultimate Query
SELECT
resources.resourcename,
... other columns that I am selecting out...
string_agg(groupname, ', ') AS Groups
FROM resources
...
JOINs to tables to get data for some of the other columns
LEFT JOIN groupresources USING (resourceid)
LEFT JOIN group USING (groupid)
LEFT JOIN resources USING (resourceid)
...
WHERE resources.companyid = 1234
GROUP BY
resourcename,
...all other columns except the one inside the string_agg function...
ORDER BY resourcename ASC;
The GROUP BY ensured that I was getting results for the groups a resource belonged to, rather than all groups within a company.
...however this was still excluding some rows as I knew the expected number of resources, and my result was less than this.
I discovered that I could achieve a similar effect using a SELECT inside a JOIN.
Final Query
SELECT
resources.resourcename,
... other columns that I am selecting out...
Groups
FROM resources
...
JOINs to tables to get data for some of the other columns
LEFT JOIN resources USING (resourceid)
LEFT JOIN
(
SELECT
resourceid,
string_agg(groupname,', ' ORDER BY groupname) AS Groups
FROM tgroupresources
JOIN tgroup USING (groupid)
GROUP BY resourceid
) AS g USING (resourceid)
...
WHERE resources.companyid = 1234
ORDER BY resourcename ASC;
Which returned the full result set.
To be blunt I don't know SQL however I don't want the answer, I want to work it out myself.
Here's the question:
Write a SQL query to calculate the number of goals for each team.
players
id name team_id goals
1 Joel 1 3
2 Ed 2 1
3 Simon 2 4
teams
id name
1 New Zealand
2 London
What I'm asking for is an arrow to information that will allow me to solve the question.
I've tried looking myself but I don't even know the correct terminology to ask the question, googling 'write sql to add fields for each row' just seems to return about adding columns or inserting.
You need to first try to JOIN your tables(id in Teams will be linked to TeamId in Players.) based on the foreign key columns.
Then you need to do the GROUP BY and use the aggregate function SUM to get the goals for each team.
So your query will be like:
select t.name, sum(p.goals) as cnt,
from players p inner join teams t on p.teamid = t.id
group by t.name
First you have to group players by teams : use t1.id=t2.id to join values in the tables, and then group theme by "BROUP BY" t.name.
Then : user "SUM(value)" function who sum values .
select teams.name,sum(players.goals) from players,team where player.team_id=teams.id group by teams.name;
I'm trying to extract info from a table in my database based on a persons job. In one table i have all the clients info, in another table linked by ID_no their job title and the branches theyre associated with. the problem I'm having is when i join both tables I'm returning some duplicates because a person can be associated with more than one branch.
I would like to know how to return the duplicated values only once, because all I care about for the moment is the persons id number and what their job title is.
SELECT *
FROM dbo.employeeinfo AS ll
LEFT OUTER JOIN employeeJob AS lly
ON ll.id_no = lly.id_no
WHERE lly.job_category = 'cle'
I know Select Distinct will not work in this situation since the duplicated values return different branches.
Any help would be appreciated. Thanks
I'm using sql server 2008 by the way
*edit to show result i would like
------ ll. ll. lly. lly.
rec_ID --employeeID---Name-----JobTitle---Branch------
1 JX100 John cle london
2 JX100 John cle manchester
3 JX690 Matt 89899 london
4 JX760 Steve 12345 london
I would like the second record to not display because i'm not interested in the branch. i just need to know the employee id and his job title, but because of how the tables are structured it's returning JX100 twice because he's recorded as working in 2 different branches
You must use SELECT DISTINCT and specify you ONLY want person id number and job title.
I don't know exactly your fields name, but I think something like this could work.
SELECT DISTINCT ll.id_no AS person_id_number,
lly.job AS person_job
FROM dbo.employeeinfo AS ll LEFT OUTER JOIN
employeeJob AS lly ON ll.id_no = lly.id_no
WHERE lly.job_category = 'cle'