sql group by values from two columns - sql

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

Related

How to groupby by aggregating different keys in SQL

I have tables like below.
I would like to groupby by generating new keys like D,Dmeans AorB
In this case,countin D is 2 becauseAandBhas 1 record each.
Are there any way to generate new keys and groupby by using this?
product sex age
A M 10
B M 20
C F 30
My desired result is like below.
product count
C 1
D (A orB) 2
If you have same experience please let me know.
Thanks
Instead of the column product you must group by a derived column that matches your condition:
select
case when product in ('A', 'B') then 'D' else product end product,
count(*)
from tablename
group by case when product in ('A', 'B') then 'D' else product end
See the demo.
Results:
| newproduct | count(*) |
| ---------- | -------- |
| C | 1 |
| D | 2 |
ANSI SQL compliant query, use a case expression in a derived table to put A and B into D. GROUP BY its result:
select product, count(*)
from
(
select case when product in ('A', 'B') then 'D' else product end product
from tablename
) dt
group by product

How to select from two different columns based on one having a null value in oracle

I have the following example data in Oracle.
Table Name
ID Name city
1 Atik 1
2 Tania null
3 Anabia 3
Table City
ID Name
1 A
2 b
3 C
I am trying to select a value from table CITY if the value in table NAME is null, otherwise I want the value in NAME. The result should look like this:
Result
ID Name city
1 Atik A
2 Tania null
3 Anabia B
This is only 3 columns, but I have a lot of columns like this.
Use a correlated subquery, in this way:
SELECT id, name,
(SELECT Name FROM City c
WHERE c.id = n.city ) as City
FROM Name n
You can also use LEFT JOIN:
SELECT n.id,
n.name,
c.name as city
FROM Name n
LEFT JOIN City c
ON c.id = n.city
Demo: https://dbfiddle.uk/?rdbms=oracle_11.2&fiddle=600cdfc1cbf08b8bc2798229f02a3d96
| ID | NAME | CITY |
|----|--------|--------|
| 1 | Atik | A |
| 2 | Tania | (null) |
| 3 | Anabia | C |
Use NVL2 to check for NULL value in table Name:
SELECT n.ID, n.Name, NVL2(n.city, c.Name, NULL) AS City
FROM Name n LEFT JOIN City c
ON c.ID = n.city
You can use a left join on the ID field, and then use a CASE statement to choose your value. It should be flexible enough to help with your other columns too.
SELECT
n.ID,
n.NAME,
CASE WHEN n.CITY IS NULL THEN NULL ELSE c.NAME END AS CITY
FROM
NAME n LEFT JOIN CITY c ON n.ID = c.ID

Query to identify records without certain values

I have a table of data that looks something like this:
ID Num | Code
-------------
1 | A
1 | B
1 | C
1 | D
2 | A
2 | B
3 | A
3 | B
3 | D
4 | B
5 | A
5 | B
5 | E
And I need to be able to write an SQL query to show me all ID Numbers that do not have Codes C or D associated with them. (Which in this example would be ID Numbers 2, 4, & 5.)
Thanks in advance for any help you can provide!
I would use NOT IN:
SELECT DISTINCT ID_Num
FROM t
WHERE ID_Num NOT IN
(SELECT ID_Num
FROM t
WHERE code = 'C'
OR code = 'D')
I like to approach this type of question using group by and having:
select id_num
from t
group by id_num
having sum(case when code in ('C', 'D') then 1 else 0 end) = 0;
you can use 'not exists' (often more performant of not in)
SELECT DISTINCT ID_Num
FROM yourtable f1
WHERE not exists
(
SELECT * FROM yourtable f2
WHERE f2.code in ('C', 'D') and f2.ID_Num=f1.ID_Num
)
you can use 'left outer join lateral' and take not founded row like this:
SELECT DISTINCT f1.ID_Num
FROM yourtable f1
LEFT OUTER JOIN LATERAL
(
SELECT f2.ID_Num FROM yourtable f2
WHERE f2.code in ('C', 'D') AND f2.ID_Num=f1.ID_Num
FETCH FIRST ROWS ONLY
) f3 on 1=1
WHERE f3.ID_Num is 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.

Using the same table twice in a FROM clause

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