Using the same table twice in a FROM clause - sql

I have a table that looks like this:
1 | 'Frank' | 'A'
2 | 'Frank' | 'B'
3 | 'Tom' | 'A'
4 | 'Tom' | 'B'
And I want an output that looks like this:
Frank | A | B
Tom | A | B
I've come up with this:
SELECT N.Name, N.Surname, M.Surname
FROM NAMES N, NAMES M
WHERE N.Surname = 'B' AND M.Surname = 'A'
GROUP BY N.Name;
This seems to work, but I don't know if it is a good practise to use the same table twice in the FROM clause, or if the performance will be affected by this on large tables.
Is there a simpler solution?

Having more than one table behind from is old-fashioned: you can make your join more readable with the standard on syntax:
from Names n
join Names m
on n.name = m.name
A self-join with a restrictive condition isn't too expensive. And Name is a pretty restrictive condition: only 2 records will share the name.
You could write it a little bit more efficient in a database that supports row_number(). That allows you to run the query without a join:
select Name
, max(case when rn = 1 then Surname end) as Surname1
, max(case when rn = 2 then Surname end) as Surname2
, max(case when rn = 3 then Surname end) as Surname3
from (
select row_number() over(
partition by Name
order by Surname) as rn
, Name
, Surname
from YourTable
) SubQueryAlias
group by
Name

Related

Finding out how two tables are connected by looking in a third

I have a couple of tables:
boxes Table ToysTable Kitchen Table
boxId | ID Name | ID Type | ID
------------- -------------- -----------
1 | TOY1 Ball | TOY1 Fork | KIT1
1 | TOY2 Car | TOY2 Knife | KIT2
1 | KIT1 Puzzle | TOY3 Spoon | KIT3
2 | KIT2
I want to find what box contains my stuff.
So if I ask:
What box contains a fork and my car toy.
I want to get the Id of that box, in this case 1.
How would I do this ?
Sqlfiddle: http://sqlfiddle.com/#!4/11b0a/3/0
Edit:
Updated column name on kitchen to type.
Edit2:
Final solution became something like this (Thanks Gordon):
select b.boxid
from boxes b left join
(select id, 'toy' type
from toys t
where t.name in ('Car', 'Fork')
union all
select id, 'kitchen' type
from kitchen k
where k.name in ('Car', 'Fork')
) tk
on b.id = tk.id
group by b.boxid
having count(distinct tk.type) = 2;
This is a bit tricky, because the id could be to either table. One solution is group by with a union all. Here is a generic approach, assuming that the ids in the two reference table have different values:
select b.boxid
from boxes b left join
(select id, name
from toys t
union all
select id, name
from kitchen k
) tk
on b.id = tk.id
group by b.boxid
having sum(case when tk.name = 'Car' then 1 else 0 end) > 0 and
sum(case when tk.name = 'Fork' then 1 else 0 end) > 0;
Note: In MySQL, I would write this query as:
select b.boxid
from boxes b left join
(select id, name
from toys t
where t.name in ('Car', 'Fork')
union all
select id, name
from kitchen k
where k.name in ('Car', 'Fork')
) tk
on b.id = tk.id
group by b.boxid
having count(distinct name) = 2;
You could write it this way in any SQL dialect, actually.

Compare Multiple rows In SQL Server

I have a SQL Server database full of the following (fictional) data in the following structure:
ID | PatientID | Exam | (NON DB COLUMN FOR REFERENCE)
------------------------------------
1 | 12345 | CT | OK
2 | 11234 | CT | OK(Same PID but Different Exam)
3 | 11234 | MRI | OK(Same PID but Different Exam)
4 | 11123 | CT | BAD(Same PID, Same Exam)
5 | 11123 | CT | BAD(Same PID, Same Exam)
6 | 11112 | CT | BAD(Conflicts With ID 8)
7 | 11112 | MRI | OK(SAME PID but different Exam)
8 | 11112 | CT | BAD(Conflicts With ID 6)
9 | 11123 | CT | BAD(Same PID, Same Exam)
10 | 11123 | CT | BAD(Same PID, Same Exam)
I am trying to write a query with will go through an identify everything that isn't bad as per my example above.
Overall, a patient (identified by PatientId) can have many rows, but may not have 2 or more rows with the same exam!
I have attempted various modifications of exams I found on here but still with no luck.
Thanks.
You seem to want to identify duplicates, ranking them as good or bad. Here is a method using window functions:
select t.id, t.patientid, t.exam,
(case when cnt > 1 then 'BAD' else 'OK' end)
from (select t.*, count(*) over (partition by patientid, exam) as cnt
from table t
) t;
use Count() over() :
select *,case when COUNT(*) over(partition by PatientID, Exam) > 1 then 'bad' else 'ok'
from yourtable
You can also use:
;WITH CTE_Patients
(ID, PatientID, Exam, RowNumber)
AS
(
SELECT ID, PatientID, Exam
ROW_NUMBER() OVER (PARTITION BY PatientID, Exam ORDER BY ID)
FROM YourTableName
)
SELECT TableB.ID, TableB.PatientID, TableB.Exam, [DuplicateOf] = TableA.ID
FROM CTE_Patients TableB
INNER JOIN CTE_Patients TableA
ON TableB.PatientID = TableA.PatientID
AND TableB.Exam = TableA.Exam
WHERE TableB.RowNumber > 1 -- Duplicate rows
AND TableA.RowNumber = 1 -- Unique rows
I have a sample here: SQL Server – Identifying unique and duplicate rows in a table, you can identify unique rows as well as duplicate rows
If you don't want to use a CTE or Count Over, you can also group the Source table, and select from there...(but I'd be surprised if #Gordon was too far off the mark with the original answer :) )
SELECT a.PatientID, a.Exam, CASE WHEN a.cnt > 1 THEN 'BAD' ELSE 'OK' END
FROM ( SELECT PatientID
,Exam
,COUNT(*) AS cnt
FROM tableName
GROUP BY Exam
,PatientID
) a
Select those patients that never have 2 or more exams of same type.
select * from patients t1
where not exists (select 1 from patients t2
where t1.PatientID = t2.PatientID
group by exam
having count(*) > 1)
Or, if you want all rows, like in your example:
select ID,
PatientID,
Exam,
case when exists (select 1 from patients t2
where t1.PatientID = t2.PatientID
group by exam
having count(*) > 1) then 'BAD' else 'OK' end
from patients

Pivot/Transpose telephone numbers stored in rows

I have seen a lot of posts and topics regarding pivots and transposing data, I'm either not understanding them (likely) or I'm trying to over complicate what I need to do.
I'm running a simple select statement like so:
SELECT Customer, Telephone
FROM DetailsTable
WHERE Customer = 74270571
GROUP BY Customer, Telephone
This returns:
Customer | Telephone
74270571 | 01556589962
74270571 | 07756563729
And what I am trying to get is
Customer | Tel1 | Tel2
74270571 | 01556589962 | 07756563729
There can be a max of 5 tel numbers.
Since you have multiple Telephone values for each Customer, the easiest way to get the result would be to use a windowing function like row_number to create a unique value for each Telephone/Customer combination. Once you have this value, then you can PIVOT the result using an aggregate function and CASE expression or the PIVOT function.
Having up to five telephone numbers, makes this easy to write the query as a hard-coded or static version. Using an aggregate function with a CASE expression the code would be:
select
customer,
Tel1 = max(case when rn = 1 then telephone else null end),
Tel2 = max(case when rn = 2 then telephone else null end),
Tel3 = max(case when rn = 3 then telephone else null end),
Tel4 = max(case when rn = 4 then telephone else null end),
Tel5 = max(case when rn = 5 then telephone else null end)
from
(
select customer, telephone,
rn = row_number() over(partition by customer order by telephone)
from DetailsTable
) x
group by customer;
See SQL Fiddle with Demo
If you want to use the PIVOT function, then the code would be:
select customer,
Tel1,
Tel2,
Tel3,
Tel4,
Tel4
from
(
select customer, telephone,
col = 'Tel'+cast(row_number() over(partition by customer order by telephone)
as varchar(1))
from DetailsTable
) x
pivot
(
max(telephone)
for col in (Tel1, Tel2, Tel3, Tel4, Tel5)
) p
See SQL Fiddle with Demo. Both give a result:
| CUSTOMER | TEL1 | TEL2 | TEL3 | TEL4 |
|----------|------------|------------|--------|--------|
| 74270571 | 1556589962 | 7756563729 | (null) | (null) |

Sql select query based on a column value

I have a Table1 like this:
ApplicableTo IdApplicable
---------------------------
Dept 1
Grade 3
section 1
Designation 2
There other tables like:
tblDept:
ID Name
1 dept1
2 baking
3 other
tblGrade:
ID Name
1 Grd1
2 Manager
3 gr3
tblSection:
id Name
1 Sec1
2 sec2
3 sec3
tblDesignation:
id Name
1 Executive
2 Developer
3 desig3
What I need is a query for table1 in such a way that gives me
ApplicableTo (table1)
Name (from the relevant table based on the value in `ApplicableTo` column)
Is this possible?
Desired Result:
eg: ApplicableTo IdApplicable Name
Dept 1 dept1
grade 3 gr3
Section 1 sec1
Designation 2 Developer.
This is the result I desire.
You could do something like the following so the applicable to becomes part of the JOIN predicate:
SELECT t1.ApplicableTo, t1.IdApplicable, n.Name
FROM Table1 AS t1
INNER JOIN
( SELECT ID, Name, 'Dept' AS ApplicableTo
FROM tblDept
UNION ALL
SELECT ID, Name, 'Grade' AS ApplicableTo
FROM tblGrade
UNION ALL
SELECT ID, Name, 'section' AS ApplicableTo
FROM tblSection
UNION ALL
SELECT ID, Name, 'Designation' AS ApplicableTo
FROM tblDesignation
) AS n
ON n.ID = t1.IdApplicable
AND n.ApplicableTo = t1.ApplicableTo
I would generally advise against this approach, although it may seem like a more consice approach, you would be better having 4 separate nullable columns in your table:
ApplicableTo | IdDept | IdGrade | IdSection | IdDesignation
-------------+--------+---------+-----------+---------------
Dept | 1 | NULL | NULL | NULL
Grade | NULL | 3 | NULL | NULL
section | NULL | NULL | 1 | NULL
Designation | NULL | NULL | NULL | 2
This allows you to use foreign keys to manage your referential integrity properly.
You can use CASE here,
SELECT ApplicableTo,
IdApplicable,
CASE
WHEN ApplicableTo = 'Dept' THEN (SELECT Name FROM tblDept WHERE tblDept.ID = IdApplicable)
WHEN ApplicableTo = 'Grade' THEN (SELECT Name FROM tblGrade WHERE tblGrade.ID = IdApplicable)
WHEN ApplicableTo = 'Section' THEN (SELECT Name FROM tblSection WHERE tblSection.ID = IdApplicable)
WHEN ApplicableTo = 'Designation' THEN (SELECT Name FROM tblDesignation WHERE tblDesignation.ID = IdApplicable)
END AS 'Name'
FROM Table1
The easiest way to achieve that is to add an extra column in table1 to keep the table where id is refferred to. Otherwise you can't know in which table the applicable id is reffered to.
Or you can create the applicable id in a way that you can extract the table afterwords from it for example a1 for id 1 in tblDept. And then use [case] (http://dev.mysql.com/doc/refman/5.0/en/case.html) (for mysql) in order to make the correct Join.

sql group by values from two columns

City | Person City | Person
-------------- -------------
C | Fanie D | Jan
C | Johannes D | Maria
C | Anna to J | Frik
D | Jan C | Anna
D | Maria C | Fanie
J | Frik C | Johannes
I want ^ table to be like this one ^.
I have tried to say Select * FROM TableA GROUP BY City ='D', City = 'J' City = 'C' but then the names are not in the order as shown above? How would I achieve this? Or how do you croup by a column value and sort the names. ?
For customized sorting, or sorting based on some rule, one can use case statement in order by clause in following way and execute it
Select * FROM TableA order by case when city ='D' then 1
when city ='J' then 2 else 3 end, Person
Can you try this ?
Try this:
SELECT City, Person
FROM TableA
WHERE City IN ('C', 'D', 'J')
ORDER BY
(CASE City
WHEN 'D' THEN 1
WHEN 'J' THEN 2
WHEN 'C' THEN 3
ELSE 4 END),
Person;
select * from TableA ORDER BY City,Person
This should work if you want to sort by City then by Person, but from your output I can't see anykind of sorting.
i dont know why you need to do this i hope this will help you!,
i used the CASE function of MySQL the solution that i provide is WHEN the City is equals to 'D' it will be the first who sort then if City = 'C' then it will be the second ELSE it will be the last
SELECT * FROM TableA GROUP BY City,Person ORDER BY CASE WHEN City = 'D' THEN 1 WHEN 'C' THEN 2 ELSE 3 END