one to many select in sql - sql

Well i have two tables first for employees and the second for emails, i want to see the employees with all his emails
table employees:
id | name | idE
1 | name1 | 1
2 | name2 | 2
3 | name3 | 3
table email:
id | idE | email
1 | 1 | e1#email.com
2 | 1 | e2#email.com
3 | 2 | e#email2.com
4 | 3 | e#email3.com
and i have this query:
select e.id, e.name, m.email from employees as e inner join email as m on p.idE = m.idE
and this is the result:
id | name | email
1 | name1 | e1#email.com
1 | name1 | e2#email.com
2 | name2 | e#email2.com
3 | name3 | e#email3.com
i dont need employee 1 twice i just need both emails in the same row
Hi guys i modified my query to:
select em.name,
STUFF(
(select ',' + e.Email from tbl_Employees as m inner join tbl_Email as e on m.idE = e.idE for xml path(''))
,1,1,'') as emails
from tbl_employees as em
but now my problem is that i get this in each column of email:
e1#email.com,e2#email.com,e#email2.com,e#email3.com,
how can i do it to get only the emails of each employee?
instead of having every single email in each column
for example:
name | email
name1 | e1#email.com,e2#email.com,
name2 | e#email2.com,
name3 | e#email3.com,
pd: sorry if i have some mistakes im still learning english

Although what you're asking for is possible, and I'm sure you'll get several answers, your question is a good example of be careful what you wish for ! You're asking the DBMS to supply some presentation-level functionality. You're usually better off letting SQL find the data you need, and handling presentation issues in your application.
SELECT returns a table of information, and the value of each column in a row is normally "atomic" in the sense that it holds a single value that SQL can compare to other values. The tables in your database have the same property if they adhere to first normal form.
One of the virtues of SQL is that "everything is a table": you can stack SELECTs one atop the other.
select * from (select a, b from T where a > b) as Z
Each inner one produces another table that the next outer one can act on.
Functions like group_concat (as an example) produce a non-atomic column. It's only a short step from there to code like:
select * from ( select group_concat(a) as a_list from T ) as Z
where a_list like '%Henry%'
The programmer is forced into search strings instead of direct comparison. The query becomes less precise and thus error-prone.
The fail-safe approach, in your example, is to read the results one by one from the DBMS, and concatenate a per-user address list by watching the idE column. It will keep your query simpler, and if you later decide to present them another way, you can do that without diving into the database logic.

this is my query after the changes
select em.name,
STUFF(
(select ',' + e.Email from tbl_Employees as m inner join
tbl_Email as e on m.idE = e.idE
where em.idE = e.idE
for xml path('')
) ,1,1,'') as emails
from tbl_employees as em
and the results of this query
name | emails
name1 | e1#email.com,e2#email.com
name2 | e#email2.com
name3 | e#email3.com
thank you for the answers!

Related

In a query (no editing of tables) how do I join data without any similarities?

I Have a query that finds a table, here's an example one.
Name |Age |Hair |Happy | Sad |
Jon | 15 | Black |NULL | NULL|
Kyle | 18 |Blonde |YES |NULL |
Brad | 17 | Blue |NULL |YES |
Name and age come from one table in a database, hair color comes from a second which is joined, and happy and sad come from a third table.My goal would be to make the first line of the chart like this:
Name |Age |Hair |Happy |Sad |
Jon | 15 |Black |Yes |Yes |
Basically I want to get rid of the rows under the first and get the non NULL data joined to the right. The problem is that there is no column where the Yes values are on the Jon row, so I have no idea how to get them there. Any suggestions?
PS. With the data I am using I can't just put a 'YES' in the 'Jon' row and call it a day, I would need to find the specific value from the lower rows and somehow get that value in the boxes that are NULL.
Do you just want COALESCE()?
COALESCE(Happy, 'Yes') as happy
COALESCE() replaces a NULL value with another value.
If you want to join on a NULL value work with nested selects. The inner select gets an Id for NULLs, the outer select joins
select COALESCE(x.Happy, yn_table.description) as happy, ...
from
(select
t1.Happy,
CASE WHEN t1.Happy is null THEN 1 END as happy_id
from t1 ...) x
left join yn_table
on x.xhappy_id = yn_table.id
If you apply an ORDER BY to the query, you can then select the first row relative to this order with WHERE rownum = 1. If you don't apply an ORDER BY, then the order is random.
After reading your new comment...
the sense is that in my real data the yes under the other names will be a number of a piece of equipment. I want the numbers of the equipment in one row instead of having like 8 rows with only 4 ' yes' values and the rest null.
... I come to the conclusion that this a XY problem.
You are asking about a detail you think will solve your problem, instead of explaining the problem and asking how to solve it.
If you want to store several pieces of equipment per person, you need three tables.
You need a Person table, an Article table and a junction table relating articles to persons to equip them. Let's call this table Equipment.
Person
------
PersonId (Primary Key)
Name
optional attributes like age, hair color
Article
-------
ArticleId (Primary Key)
Description
optional attributes like weight, color etc.
Equipment
---------
PersonId (Primary Key, Foreign Key to table Person)
ArticleId (Primary Key, Foreign Key to table Article)
Quantity (optional, if each person can have only one of each article, we don't need this)
Let's say we have
Person: PersonId | Name
1 | Jon
2 | Kyle
3 | Brad
Article: ArticleId | Description
1 | Hat
2 | Bottle
3 | Bag
4 | Camera
5 | Shoes
Equipment: PersonId | ArticleId | Quantity
1 | 1 | 1
1 | 4 | 1
1 | 5 | 1
2 | 3 | 2
2 | 4 | 1
Now Jon has a hat, a camera and shoes. Kyle has 2 bags and one camera. Brad has nothing.
You can query the persons and their equipment like this
SELECT
p.PersonId, p.Name, a.ArticleId, a.Description AS Equipment, e.Quantity
FROM
Person p
LEFT JOIN Equipment e
ON p.PersonId = e.PersonId
LEFT JOIN Article a
ON e.ArticleId = a.ArticleId
ORDER BY p.Name, a.Description
The result will be
PersonId | Name | ArticleId | Equipment | Quantity
---------+------+-----------+-----------+---------
3 | Brad | NULL | NULL | NULL
1 | Jon | 4 | Camera | 1
1 | Jon | 1 | Hat | 1
1 | Jon | 5 | Shoes | 1
2 | Kyle | 3 | Bag | 2
2 | Kyle | 4 | Camera | 1
See example: http://sqlfiddle.com/#!4/7e05d/2/0
Since you tagged the question with the oracle tag, you could just use NVL(), which allows you to specify a value that would replace a NULL value in the column you select from.
Assuming that you want the 1st row because it contains the smallest age:
- wrap your query inside a CTE
- in another CTE get the 1st row of the query
- in another CTE get the max values of Happy and Sad of your query (for your sample data they both are 'YES')
- cross join the last 2 CTEs.
with
cte as (
<your query here>
),
firstrow as (
select name, age, hair from cte
order by age
fetch first row only
),
maxs as (
select max(happy) happy, max(sad) sad
from cte
)
select f.*, m.*
from firstrow f cross join maxs m
You can try this:
SELECT A.Name,
A.Age,
B.Hair,
C.Happy,
C.Sad
FROM A
INNER JOIN B
ON A.Name = B.Name
INNER JOIN C
ON A.Name = B.Name
(Assuming that Name is the key columns in the 3 tables)

Novice seeking help, Max Aggregate not returning expected results

I'm still very new to MS-SQL. I have a simple table and query that that is getting the best of me. I know it will something fundamental I'm overlooking.
I've changed the field names but the idea is the same.
So the idea is that every time someone signs up they get a RegID, Name, and Team. The names are unique, so for below yes John changed teams. And that's my trouble.
Football Table
+------------+----------+---------+
| Max_RegID | Name | Team |
+------------+----------+---------+
| 100 | John | Red |
| 101 | Bill | Blue |
| 102 | Tom | Green |
| 103 | John | Green |
+------------+----------+---------+
With the query at the bottom using the Max_RegID, I was expecting to get back only one record.
+------------+----------+---------+
| Max_RegID | Name | Team |
+------------+----------+---------+
| 103 | John | Green |
+------------+----------+---------+
Instead I get back below, Which seems to include Max_RegID but also for each team. What am I doing wrong?
+------------+----------+---------+
| Max_RegID | Name | Team |
+------------+----------+---------+
| 100 | John | Red |
| 103 | John | Green |
+------------+----------+---------+
My Query
SELECT
Max(Football.RegID) AS Max_RegID,
Football.Name,
Football.Team
FROM
Football
GROUP BY
Football.RegID,
Football.Name,
Football.Team
EDIT* Removed the WHERE statement
The reason you're getting the results that you are is because of the way you have your GROUP BY clause structured.
When you're using any aggregate function, MAX(X), SUM(X), COUNT(X), or what have you, you're telling the SQL engine that you want the aggregate value of column X for each unique combination of the columns listed in the GROUP BY clause.
In your query as written, you're grouping by all three of the columns in the table, telling the SQL engine that each tuple is unique. Therefore the query is returning ALL of the values, and you aren't actually getting the MAX of anything at all.
What you actually want in your results is the maximum RegID for each distinct value in the Name column and also the Team that goes along with that (RegID,Name) combination.
To accomplish that you need to find the MAX(ID) for each Name in an initial data set, and then use that list of RegIDs to add the values for Name and Team in a secondary data set.
Caveat (per comments from #HABO): This is premised on the assumption that RegID is a unique number (an IDENTITY column, value from a SEQUENCE, or something of that sort). If there are duplicate values, this will fail.
The most straight forward way to accomplish that is with a sub-query. The sub-query below gets your unique RegIDs, then joins to the original table to add the other values.
SELECT
f.RegID
,f.Name
,f.Team
FROM
Football AS f
JOIN
(--The sub-query, sq, gets the list of IDs
SELECT
MAX(f2.RegID) AS Max_RegID
FROM
Football AS f2
GROUP BY
f2.Name
) AS sq
ON
sq.Max_RegID = f.RegID;
EDIT: Sorry. I just re-read the question. To get just the single record for the MAX(RegID), just take the GROUP BY out of the sub-query, and you'll just get the current maximum value, which you can use to find the values in the rest of the columns.
SELECT
f.RegID
,f.Name
,f.Team
FROM
Football AS f
JOIN
(--The sub-query, sq, now gets the MAX ID
SELECT
MAX(f2.RegID) AS Max_RegID
FROM
Football AS f2
) AS sq
ON
sq.Max_RegID = f.RegID;
Use row_number()
select * from
(SELECT
Football.RegID AS Max_RegID,
Football.Name,
Football.Team, row_number() over(partition by name order by Football.RegID desc) as rn
FROM
Football
WHERE
Football.Name = 'John')a
where rn=1
simply you can edit your query below way
SELECT *
FROM
Football f
WHERE
f.Name = 'John' and
Max_RegID = (SELECT Max(Football.Max_RegID) where Football.Name = 'John'
)
or
if sql server simply use this
select top 1 * from Football f
where f.Name = 'John'
order by Max_RegID desc
or
if mysql then
select * from Football f
where f.Name = 'John'
order by Max_RegID desc
Limit 1
You need self join :
select f1.*
from Football f inner join
Football f1
on f1.name = f.name
where f.Max_RegID = 103;
After re-visit question, the sample data suggests me subquery :
select f.*
from Football f
where name = (select top (1) f1.name
from Football f1
order by f1.Max_RegID desc
);

SQL - Group by Elements of Comma Delineation

How can I group by a comma delineated list within a row?
Situation:
I have a view that shows me information on support tickets. Each ticket is assigned to an indefinite number of resources. It might have one name in the resource list, it might have 5.
I would like to aggregate by individual names, so:
| Ticket ID | resource list
+-----------+----------
| 1 | Smith, Fred, Joe
| 2 | Fred
| 3 | Smith, Joe
| 4 | Joe, Fred
Would become:
| Name | # of Tickets
+-----------+----------
| Fred | 3
| Smith | 2
| Joe | 3
I did not design the database, so I am stuck with this awkward resource list column.
I've tried something like this:
SELECT DISTINCT resource_list
, Count(*) AS '# of Tickets'
FROM IEG.vServiceIEG
GROUP BY resource_list
ORDER BY '# of Tickets' DESC
...which gives me ticket counts based on particular combinations, but I'm having trouble getting this one step further to separate that out.
I also have access to a list of these individual names that I could do a join from, but I'm not sure how I would make that work. Previously in reports, I've used WHERE resource_list LIKE '%' + #tech + '%', but I'm not sure how I would iterate through this for all names.
EDIT:
This is my final query that gave me the information I was looking for:
select b.Item, Count(*) AS 'Ticket Count'
from IEG.vServiceIEG a
cross apply (Select * from dbo.Split(REPLACE(a.resource_list, ' ', ''),',')) b
Group by b.Item
order by 2 desc
Check this Post (Function Definition by Romil) for splitting strings into a table:
How to split string and insert values into table in SQL Server
Use it this way :
select b.Item, Count(*) from IEG.vServiceIEG a
cross apply (
Select * from dbo.Split (a.resource_list,',')
) b
Group by b.Item
order by 2 desc

How to join (without concatenating) two result sets of two fields together as one long ‘list’

What SQL Code do I require to perform the following? :
I have one table (lets arbitrarily call the table 'Names'):
ID | Name1 | Name2
---+--------+-------
1 | Fred | Jack
2 | Jack | Jim
3 | Jill | Fred
4 | Jim | Jack
etc
What I'd like is to produce is a single list of Name1 and Name2 (I don't even care about Grouping or Ordering) as such, but I would like to keep the original 'ID' association with the name:
ID | Names
---+------
1 | Fred
1 | Jack
2 | Jill
2 | Jim
3 | Jack
3 | Jim
4 | Fred
4 | Jack
Why do I want to do this? Because it looks easy and as a SQL coder I should probably be able to perform this task, but I can't figure out a solution that will create this output. Further more I've only manage to find people with the desire to concatenate the fields, which is a simple task, but I'm not interested in concatenation.
Additional Question: Would the SQL query be vastly different if Name1 field was in a different table to Name2? (if it is different, what would it look like?)
Additional Question: Would the SQL query be simpler if we didn't care about the ID field? If so, what would that look like.
You can use this form to include the id, and give you a specific ordering by ID.
SELECT n.id, n.name1 FROM names n
UNION
SELECT m.id, m.name2 from names m
ORDER BY id ASC;
If it were in a different table, the use of UNION doesn't have to change, since we're bringing the results from the table together, and ordering them by ID. This doesn't mean that the data is related, though.
SELECT n.id, n.name FROM name_one n
UNION
SELECT m.id, m.name from name_two m
ORDER BY id ASC;
If we didn't care about the ID field, it would be ever so lightly simpler - it's just selecting one column at that point.
SELECT ID, Name1 as Names FROM person
UNION
SELECT ID, Name2 as Names FROM person
?
. . What you mean by "without concatenating"?

Building a SQL query that doesn't include data based on hierarchy

I've spent quite a bit of time on Google and SO the last few days, but I can't seem to find an answer to my problem. Not knowing exactly how to phrase the issue into a reasonable question makes it a little more difficult. You don't know what you don't know, right?
Due to business limitations I can't post my exact code and database structure, so I'll do my best to give a solid example.
Customer Table - Holds customer data
[CustId] | [CustName]
---------------------
1 | John Smith
2 | Jane Doe
3 | John Doe
Code Table - Holds code data
[CodeId] | [CodeDesc]
---------------------
A | A Desc
B | B Desc
C | C Desc
D | D Desc
E | E Desc
CustomerCode Table - Combines customers with codes
[CustId] | [CodeId]
-------------------
1 | A
1 | B
2 | B
2 | C
2 | D
3 | C
3 | E
CodeHierarchy Table - Hierarchy of codes that shouldn't be included (DropCode) if a customer has a ConditionCode
[ConditionCode] | [DropCode]
----------------------------
A | B
B | C
B | D
Now I'll try to explain my actual question.
What I'm trying to accomplish is writing a query (view) that will list codes based on the CodeHierarchy table.
Results would be something like this:
[CustName] | [CodeId]
-------------------
John Smith | A
Jane Doe | B
John Doe | C
John Doe | E
Code B isn't listed for John Smith since he has code A. Codes C and D aren't listed for Jane Doe since she also has code B. John Doe has all codes listed (notice that E isn't even in the CodeHierarchy table).
I've tried a few different things (inner joins, left/right joins, subqueries, etc) but I just can't get the results I'm looking.
As a base query, this returns all codes:
SELECT
Customer.CustomerName,
Code.CodeDesc
FROM
Customer
INNER JOIN CustomerCode
ON Customer.CodeId = CustomerCode.CodeId
INNER JOIN Code
ON CustomerCode.CodeId = Code.CodeId
This only returns codes that are ConditionCodes (I understand why, but I though it might be worth a shot at the time):
SELECT
Customer.CustomerName,
Code.CodeDesc
FROM
Customer
INNER JOIN CustomerCode
ON Customer.CodeId = CustomerCode.CodeId
INNER JOIN Code
ON CustomerCode.CodeId = Code.CodeId
INNER JOIN CodeHierarchy
ON Customer.CodeId = CodeHierarchy.ConditionCode
AND Customer.CodeId != CodeHierarchy.DropCode
I tried a subquery (don't have that code available) that ended up dropping all DropCodes, regardless if a member did or did not have a qualifying hierarchy (i.e. customer rows with B weren't returned even if they didn't have A)
I had an idea of making the base query above a subquery and the joining it with the CodeHierarchy table, but I'm stuck on how to write the query:
SELECT
*
FROM
(
base query (with all codes)
) CustomerCodesAll
INNER/LEFT JOIN CodeHierarchy
ON ?
I've also been doing some reading on CTEs, but I'm not sure how I could make use of that technique.
This will end up being a view to be queried against for reporting purposes. The customer table contains much more data including dob, gender, company status, etc. The view will be straightforward and pull everything. Queries against the view will include where clauses for dob, gender, etc.
Can anyone point me in the right direction?
Thanks for any help.
SELECT
Customer.CustName,
Code.CodeDesc
FROM
Customer
INNER JOIN CustomerCode AS posCustomerCode
ON Customer.CustId = posCustomerCode.CustId
INNER JOIN Code
ON posCustomerCode.CodeId = Code.CodeId
LEFT JOIN CodeHierarchy
ON posCustomerCode.CodeId = CodeHierarchy.DropCode
WHERE
CodeHierarchy.ConditionCode NOT IN (
SELECT CodeId
FROM CustomerCode AS negCustomerCode
WHERE negCustomerCode.CustId=posCustomerCode.CustId
)
OR CodeHierarchy.ConditionCode IS NULL
SQLfiddle