Non-Equi Self Join - sql

I have the following user table
USERS
name city
A New York
B Paris
C London
D London
E Paris
I want to select the two users from the same City using non equi self join
such that the result follows
name name city
B E Paris
C D London

A "self-join" is just like a normal join execpet that the same table appears on both sides of the join clause. "non-equi" means finding rows which don't match on some column.
So in your case you need to join on CITY and filter on differences in NAME:
select t1.name as t1_name
, t2.name as t2_name
, t1.city
from users t1
join users t2
on t2.city = t1.city
where t1.name < t2.name
Note the filter condition is less than: using not equals would double the result set.(*)
Obviously this solution will work where CITY has two entries. If there are more than two entries you will still get multiple rows (one per combination).
(*) Some times using != is desirable: if we're investigating a data quality issue then returning all columns from both rows can help us understand what's going on.

You can use below way with the use of cte also
WITH CITY
AS (
SELECT ROW_NUMBER() OVER(PARTITION BY A.CITY ORDER BY A.CITY) RNO,A.NAME,A.CITY FROM Table1 A
)
SELECT A.NAME,B.NAME,A.CITY FROM
CITY A JOIN
CITY B
ON
A.city=B.city AND A.NAME<>B.name AND A.RNO<=B.RNO

Related

Sql to get all cities in a country given a tree type databsae

I have been given a table like this
Id Name Type ParentId
1 US country -1
2 NY state 1
3 NYC city 2
4 Yonkers city 2
5 Washington state 1
6 Seattle city 5
7 Tacoma city 5
8 Canada country -1
9 Manitoba state 8
I want to write a sql query to write the all cities in a state.
Example
Country state city
US NY NYC
US NY Yonkers
I get that I need to write a recursive query but not able yo do so. I need help to write a sql for this.
You can use a recursive common table expression:
with recursive cte as (
select id, name, type, parentid
from the_table
where type = 'state'
and name = 'NY'
union all
select c.id, c.name, c.type, c.parentid
from the_table c
join cte p on p.id = c.parentid
)
select *
from cte
where type <> 'state';
The above is standard ANSI SQL, but not all database products support this exact syntax.
If the number of levels is fixed (so it's always Country -> State -> City) and will never change, you can use a simpler query:
select c.*
from the_table c
where parentid in (select s.id
from the_table s
where s.type = 'state'
and s.name = 'NY');
SELECT t1.name country, t2.name state, t3.name city
FROM table t1
JOIN table t2 ON t1.id = t2.parent_id
JOIN table t3 ON t2.id = t3.parent_id
WHERE t2.name = 'NY';

How to join few table and connect id with name column from another table

I have these tables with the following column names:
players:
id first_name last_name age position salary hire_date skills_data_id team_id
Skills:
id, dribbling, pace, passing, shooting, speed, strength
towns:
id, name, country_id
teams:
id name established fan_base stadium_id
On this base, I have found the players with the max speed in terms of towns where their team played.
At the same time, I have to skip players that played in team ‘Devify’.
At the moment I have tried with this code, but the final result is not correct.
select max(s.speed) as `max_speed`,tt.name as `town_name`
from skills_data as s
right join players as p on s.id = p.skills_data_id
inner join teams as t on p.team_id = t.id
inner join towns as tt on p.team_id = tt.id
where t.name not like 'Devify'
group by s.id
order by max(s.speed) desc, town_name;
The result should be something like that:
max_speed town_name
97 Smolensk
92 Bromma
92 Lühua
...
NULL Zavolzh’ye
My result is:
max_speed town_name
97 Montréal-Ouest
92 Dalubian
92 Samsan
Thank you in advance.
There is a problem in your group by clause: you should be grouping by town rather than by skill id.
I am also quite suspicious about the join condition that brings in the towns (see my comment under your question): but if you are getting results for your existing query, it must be right...
Other changes to your query:
changed your right join to an inner join
used more meaningful table aliases
used an inequality condition instead of not like to exclude the unwanted team
New query:
select
max(sk.speed) as max_speed,
to.name as `town_name`
from skills_data as sk
inner join players as pl on sk.id = pl.skills_data_id
inner join teams as te on pl.team_id = te.id and te.name <> 'Devify'
inner join towns as to on pl.team_id = to.id
group by to.id, to.name
order by max_speed desc, town_name;

Which Join for SQL plus query

I have 4 tables, I would like to select one column from each table, but only if the department has both 'Mick' and 'Dave working in it (must have both names, not one or the other). But it does not seem to be working properly:
SELECT SCHOOL_NAME, TOWN, COUNTY
FROM STUDENTS
NATURAL JOIN SCHOOLS NATURAL JOIN TOWNS NATURAL JOIN
COUNTIES
WHERE FIRST_NAME IN ('Mick','Dave)
/
I'm going wrong somewhere (probably lots of places :( ). Any help would be great
Don't use NATURAL JOIN. It is an abomination, because it does not take properly declared foreign key relationships into account. It only looks at the names of columns. This can introduce really hard to find errors.
Second, what you want is aggregation:
select sc.SCHOOL_NAME, t.TOWN, c.COUNTY
from STUDENTS st join
SCHOOLS sc
on st.? = sc.? join
TOWNS t
on t.? = ? join
COUNTIES c
on c.? = t.?
where FIRST_NAME in ('Mick', 'Dave')
group by sc.SCHOOL_NAME, t.TOWN, c.COUNTY
having count(distinct st.first_name) = 2;
The ? are placeholders for table and column names. If you are learning SQL, it is all the more important that you understand how columns line up for joins in different tables.
A where clause can only check the values in a single row. There is a separate row for each student, so there is no way -- with just a where -- to find both students. That is where the aggregation comes in.
You need at least three Join conditions, and properly end the string Dave with quote :
SELECT SCHOOL_NAME, TOWN, COUNTY
FROM SCHOOLS h
JOIN TOWNS t ON (t.id=h.town_id)
JOIN COUNTIES c ON (t.county_id=c.id)
WHERE EXISTS ( SELECT school_id
FROM STUDENTS s
WHERE s.first_name in ('Mick','Dave')
AND school_id = h.id
GROUP BY school_id
HAVING count(1)>1
);
SQL Fiddle Demo
You can use an analytic function in a sub-query to count the students who have the name Mick or Dave for each school_id (assuming that is your identifier for a school):
SELECT SCHOOL_NAME, TOWN, COUNTY
FROM ( SELECT *
FROM (
SELECT d.*,
COUNT(
DISTINCT
CASE WHEN FIRST_NAME IN ( 'Mick', 'Dave' ) THEN FIRST_NAME END
) OVER( PARTITION BY school_id )
AS num_matched
FROM STUDENTS d
)
WHERE num_matched = 2
)
NATURAL JOIN SCHOOLS
NATURAL JOIN TOWNS
NATURAL JOIN COUNTIES;
SQLFiddle
You would also be better to use an INNER JOIN and explicitly specify the join condition rather than relying on NATURAL JOIN.

TSQL Understanding the execution of the Query

I have two tables
Table Visitor
ID Name CityName
1 Jon NY
1 Jon KY
2 Paul NY
1 paul TY
Table City
ID CityName
1 NY
2 KY
3 TY
I have to list the visitor who has visited all cities in City Table.
I took the query from web,but i do not the how it works internally.
The query is
select distinct v1.name from Visitor v1
where not exists
(
select c.CityName from City c
where not exists
(
select v2.CityName from visitor v2 where
v2.CityName=c.CityName and v1.Name=v2.Name
)
)
Kinldy help me to understand the step
Iteration 1
Most outer query v1.Name=jon
outer query c.CityName=NY
inner query V2.CityName = c.CityName (NY=NY)
and v1.Name=v2.Name (Jon =Jon)
inner query return valye (i.e) v2.CityName=NY
NY not exists
(
NY
)
consition fails so nothing return
Iteration 2
Most outer query v1.Name=jon
outer query c.CityName=NY
inner query V2.CityName = c.CityName (KY=NY)
and v1.Name=v2.Name (Jon =Jon)
inner query return valye (i.e) v2.CityName=NULL
NY not exists
(
NULL
)
condition fails
(i.e) NOT Exists is true so Jon is returned from outer most query.
Am i right in understanding?
This is the relational division query popularised by Chris Date.
It basically uses a double negative. To rewrite what it is doing in English: It Selects all the Visitors where there is no city that they have not visited.
FYI, a simpler approach is:
select v1.name
from Visitor v
inner join City c on v.CityName = c.CityName
group by v1.Name
having count(distinct(c.CityName)) = 3

SQL query or sub-query?

I've got a table of student information in MySQL that looks like this (simplified):
| age : int | city : text | name : text |
-----------------------------------------------------
| | | |
I wish to select all student names and ages within a given city, and also, per student, how many other students in his age group (that is, how many students share his age value).
I managed to do this with a sub-query; something like:
select
name,
age as a,
(select
count(age)
from
tbl_students
where
age == a)
from
tbl_students
where
city = 'ny'
But it seems a bit slow, and I'm no SQL-wiz, so I figure I'd ask if there's a smarter way of doing this. The table is indexed by age and city.
select
t1.name,
t1.age as a,
count(t2.age) NumberSameAge
from
tbl_students t1 inner join tbl_students t2
on t1.age=t2.age
where
city = 'ny'
group by t1.name, t1.age
not tested, but something like that. I.o.w. a groupby on a join. This sometimes can be faster as the query you're running is doing a nested subquery for every row returned, and the query I posted above (or at least, the structure with a join and a groupby) performs a query on the related students just once.
It might be easier to grab a sub-query that grabs everything at once (vs. 1000 rows where it runs the sub-query 1000 times).
SELECT Age, count(*) AS SameAge FROM tbl_students
Making the full query:
SELECT t.Name, t.Age, s.SameAge
FROM tbl_students t
INNER JOIN (
SELECT Age, count(*) AS SameAge FROM tbl_students
) AS s
ON (t.Age = s.Age) -- m:1
WHERE t.City = 'NY'