mySQL self join elegantly - mysql

My question is a bit dummy, but SQL has never been my cup of tea.
I have a simple table with 3 columns: id, name, parent_id (referring to an id in the same table).
I just want to get the relevant results to display them in an HTML page:
ID / Name / Parent Name
I can query as follows:
select id, name, parent_id from fields
union
select a.id, a.name, b.name
from fields a, fields b
where b.parent_id = a.id;
and do some tests, but i can figure out that it exists some more elegant manner.
Thx.

This query is best achieved by using a LEFT JOIN. This way you can still return fields which do not have a parent_id, i.e. are NULL. You'll also want to select the parent_name using an alias (parent_name in the example). This will allow you to programatically refer to the result by it's name rather than the numerical column index ($row->parent_name vs $row[2] if PHP is your language of choice).
SELECT f1.id, f1.name, f2.name parent_name
FROM fields f1
LEFT JOIN fields f2 ON f2.id = f1.parent_id
http://sqlfiddle.com/#!2/a9632/1/0

SELECT A.id AS ID, A.name AS Name, B.name AS Parent
FROM fields A, fields B
WHERE A.parent_id = B.id

Related

mysql select columns and sum from other table

I have the following tables:
A: id, name, url
B: Aid, number
number can be any int
(Aid is the id from table A)
I want to select the name, url from table A and the SUM(B.number)
So the results will be something like:
name, url, SUM(B.number)
name, url, SUM(B.number)
name, url, SUM(B.number)
[..]
I need to join them somehow? How do i construct that query?
SELECT name, url, (SELECT SUM(number) FROM B WHERE B.Aid = A.id) As total
FROM A
SELECT A.name, A.url, SUM(B.number)
FROM A
LEFT JOIN B ON A.id = B.Aid
GROUP BY A.name, A.url
Please test it before cause I might have made a mistake since I got no MySQL DB available at the moment.

Split a MYSQL string into an ( array, like, expression, list) that IN () can understand

I have created two tables:
A (A.id,A.name)
B (B.A_ids)
B.A_ids is fields of table A.id that are concatenated with ,.
Now I want to query the name where A.id is in B.A_ids, just like this:
select A.name from A where A.id in(1,2,3) ;
However, 1,2,3 is a query from B.A_ids.
You can use something like this:
SELECT A.* FROM A JOIN B
WHERE (
B.a_ids LIKE CONCAT(a.id,',%')
OR B.a_ids LIKE CONCAT('%,',a.id,',%')
OR B.a_ids LIKE CONCAT('%,',a.id)
OR B.a_ids = a.id
)
AND B.... = ...
The performance can be bad. For a more efficient design, you should store the one-to-many or many-to-many relationship in another table.
I finally find a way myself using find_in_set:
select distinct(b.id),
a.id,
a.name,
b.a_ids
from A
where find_in_set(a.id,b.a_ids);

Selection SQL where two conditions must be true

What's the most efficient way to select rows that must satisfy two conditions met in the same column?
name | title
------------------
John | Associate
John | Manager
Fran | Manager
Fran | President
I'd like to do something like
select name
from table
where title = 'Associate'
and name in ( select *
from table
where title = 'Manager')
which should return
John
but that seems woefully inefficient, especially if the table itself is super big. You could also do
select name
from table a,
table b
where a.title = 'Associate'
and b.title = 'Manager'
and a.name = b.name
Is that the best one can do?
Your first query is not syntactically correct. It should be:
select name
from table
where title = 'Associate' and
name in (select name from table where title = 'Manager');
The second is better written as a join:
select name
from table a join
table b
on a.title = 'Associate' and b.title = 'Manager' and a.name = b.name;
The second is probably better in terms of taking advantage of indexes on the table.
You can also do this with a group by:
select name
from table a
group by name
having sum(title = 'Associate') > 0 and sum(title = 'Manager') > 0;
MySQL is not very good at optimizing group by. But if there are no indexes on the table, it might be faster than the join methods.
I would have an index on your table via ( title, name ), then do a self-join. In this case, I am putting what would be less probable as the outer primary condition of the query where the manager records are considered first... ie. a company may have 5 managers and 100 associates vs looking for 100 associates that match the 5 managers.
select
t.name
from
table t
join table t2
on t2.title = 'Associate'
AND t.name = t2.name
where
t.title = 'Manager'
There's not a whole lot of data given as an example, but I'm assuming both John's here are the same person with multiple titles? If that were the case you would be better off having your titles being a child table of the employees table (if that's what this table is)
So instead you could have:
Employee
----
id
name
titles
----
id
titleName
employeeTitles
----
employeeId
titleId
If you can't do it this way, i would think another way to write your original query would be:
select name
from table t1
inner join (
select distinct name
from table
where title = 'manager'
) t2 on t1.name = t2.name
where title = 'Associate'
could also do group by name rather than distinct. But still, doing the above solution i think would be better all around (assuming my own assumptions are correct about your data)
Using WHERE EXISTS (or NOT EXISTS) is a very effective alternative for this
select
name
from table1
where title = 'Associate'
and exists (
select 1 /* could be: select NULL as nothing actually needs to be "returned */
from table1 as t2
where t2.title = 'Manager'
and t2.name = table1.name /* correlated here */
)
;
Similar to using IN() it requires a subquery but "correlates" that subquery. However this subquery does NOT need to move any data (IN can require this).
Also, similar to IN() using EXISTS has no impact on the number of result rows. Joins can create unwanted row repetition (but of course sometimes the extra rows are needed).
This reference is for SQL Server, but it compares several relevant methods (ignore Outer Apply - mssql specific), including potential issues dealing with NULLs when using IN() that do not affect EXISTS
| NOT | EXISTS () should be one of the first methods to consider.
It depends on MySQL Version (MySQL 5.6 has query rewrite feature which improves IN() subquery) and Table Relationships.
There are at least 3 ways to get result you're expecting. In my experience, INNE JOIN is faster than others in general cases.
Try your self with your data.
IN () - you've wrote firstly.
please note that in MySQL. IN() produces dependent sub-query plan.
SELECT DISTINCT name
FROM table
WHERE title = 'Associate'
AND name IN (SELECT name FROM table WHERE title = 'Manager')
SELF JOIN - your 2nd
SELECT DISTINCT name
FROM table t1 INNER JOIN table t2
WHERE a.title = 'Associate' AND b.title = 'Manager'
AND t1.name = t2.name
EXISTS - Semi JOIN
EXISTS is fast when tables have 1:n relationship. This requires no DISTINCT, GROUP BY.
SELECT name
FROM table t1
WHERE a.title = 'Associate'
AND EXISTS (SELECT 1 FROM table t2
WHERE t2.name = t1.name AND t2.title = 'Manager')

how to prevent duplicated rows in access 2007

now i am working with three tables,A,B and C:
A table structure:
ID,
Name,
Age
B table structure:
ID,
A.ID-> foreign key,
Hospital Name
C table structure:
ID,
A.ID->foreign key,
Drug Name
so the relation between A and B is one to many and also the same with A and C
when i make any query to find how many persons in my database i found duplicated rows
actually its not duplicated but it has for example 2 rows with the child table like: the one person has 2 records in table B so the result doesn't reflect the actual number of matched record because its linked with child tables .
Question is : how to prevent duplication in case like that?
You can use Distinct or a subquery.
SELECT DISTINCT a.ID, a.Name
FROM a
INNER JOIN b
ON a.ID = b.aID
WHERE b.Hospital = 123
Or
WHERE b.Hospital IN ( 123, 456 )
Also
SELECT a.ID, a.Name
FROM a
INNER JOIN (SELECT aID, Hospital FROM b) x
ON a.ID = x.aID
WHERE x.Hospital - 123
And
SELECT a.ID, a.Name
FROM a
WHERE a.ID
IN ((SELECT aID FROM b WHERE Hospital = 123))

need to show only the duplicate value in sql

i have table as
id----name----roll-----class
1----ram-------1-----2
2----shyam-----2-----3
3----ram-------1-----3
4----shyam-----2-----3
5----ram-------1-----2
6----hari------1-----5
i need to find the the duplicate row only that have common name, roll, class. so the expected result for me is.
id----name----roll-----class
1----ram-------1-------2
2----shyam-----2-------3
4----shyam-----2-------3
5----ram-------1-------2
i tried to get from the query below but here only one field is supported. i need all three field common. Please do help me in this. thanks
SELECT *
FROM table
WHERE tablefield IN (
SELECT tablefield
FROM table
GROUP BY tablefield
HAVING (COUNT(tablefield ) > 1)
)
You can use count() over().
select id, name, roll, class
from (select id, name, roll, class,
count(*) over(partition by name, roll, class) as c
from YourTable) as T
where c > 1
order by id
https://data.stackexchange.com/stackoverflow/query/63720/duplicates
this will retun only the duplicate entry one time:
select t.id, t.name, t.roll, t.class
from table t
inner join table t1
on t.id<t1.id
and t.name=t1.name
and t.roll = t1.roll
and t.class=t1.class
this will return what you require:
select distinct t.id, t.name, t.roll, t.class
from table t
inner join table t1
on t.name=t1.name
and t.roll = t1.roll
and t.class=t1.class
I'd suggest something like this
SELECT A.* FROM
Table A LEFT OUTER JOIN Table B
ON A.Id <> B.Id AND A.Name = B.Name AND A.Roll = B.Roll AND A.Class = B.Class
WHERE B.Id IS NOT NULL
Something like that should work (I did not test though):
select a1.*
from table a1, a2
where (a1.id != a2.id)
and (a1.name == a2.name)
and (a1.roll== a2.roll)
and (a1.class== a2.class);
It seems there are several proprosals here. If it is a query that you'll use in your code, beware of the cost of the queries. Try an 'explain' with your database.