Finding and Replacing Duplicates With Correct Data in MySQL - mysql

I have the following table with duplicate family IDs but different number of family members:
tbl_family
+------------+--------------+---------+
| familyID | Members | Location|
+------------+--------------+---------+
| 100 | 3 | xyz |
| 100 | 4 | xyz |
| 101 | 1 | abc |
| 101 | 2 | abc |
| 102 | 5 | efg |
| 103 | | hij |
+------------+--------------+---------+
I also have a second table where we verified the correct count of family members for the duplicates
tbl_verifier
+------------+--------------+---------+
| familyID | Members | Location|
+------------+--------------+---------+
| 100 | 3 | xyz |
| 101 | 2 | abc |
+------------+--------------+---------+
I want to create a view in mysql which will display the families without duplicates and maintain the row with the verified count of family members. The results should look as follows:
tbl_results
+------------+--------------+---------+
| familyID | Members | Location|
+------------+--------------+---------+
| 100 | 3 | xyz |
| 101 | 2 | abc |
| 102 | 5 | efg |
| 103 | | hij |
+------------+--------------+---------+
I am breaking the problem into several steps. I want to first select all those with matching Members then those with null Members
/* Step 1: Select only those that are matching family members count in
verifier and family */
select *
from tbl_family f
inner join
tbl_verifier v
ON f.familyID = v.familyID
WHERE f.Members = v.Members;
/* Step 2 : Select only those that have null number of rooms*/
select *
from tbl_family f
left join
tbl_verifier v
ON f.familyID = v.familyID
WHERE f.Members is null
Now am a bit stuck on how to proceed further.

Use UNION All to merge two result sets
select *
from tbl_family f
inner join tbl_verifier v ON f.familyID = v.familyID
and f.Members = v.Members
union all
select * from tbl_family f
left join tbl_verifier v ON f.familyID = v.familyID and and f.Members = v.Members
where v.familyID is null

how that sounds? I think it works
SELECT f.*
FROM tbl_family f, tbl_verifier v
WHERE (f.familyID = v.familyID AND f.Members = v.Members)
OR f.familyID NOT IN (SELECT familyID FROM tbl_verifier)

Use UNION ALL for your case, but it is better to add a location filter for same.
; with cte as (
select tbl_family.family_id, tbl_family.Members, tbl_family.location
from tbl_family
inner join tbl_verifier ON tbl_family.familyID = tbl_verifier.familyID
and tbl_family.Members = tbl_verifier.Members and tbl_family.Location = tbl_verifier.Location
union all
select tbl_family.family_id, tbl_family.Members, tbl_family.location
from tbl_family
left join tbl_verifier ON tbl_family.familyID = tbl_verifier.familyID
where tbl_family.Members is null
)
Select * from cte order by family_id

UNION ALL seems like a reasonable solution. The big question is how to get one row out of the first table if there are multiple rows.
Here is one method that uses MAX():
select v.familyID, v.Members, v.Location
from tbl_verifier v
union all
select f.familyID, max(f.Members), f.Location
from tbl_family f
where not exists (select 1
from tbl_verifier v
where v.familyId = f.familyId
);
Your question is unclear as to whether families can be in multiple locations. If so, you need to include location in the correlation clause.

From the different answers posted above, i was able to come up with the following script which worked
; with cte as (select f.familyID,
f.Members,
f.Location
from tbl_family f
inner join tbl_verifier v ON f.familyID = v.familyID
and f.Members = v.Members
union all
select f.familyID,
f.Members,
f.Location from tbl_family f
left join tbl_verifier v ON f.familyID = v.familyID
where f.Members is NULL
)
SELECT * INTO temp1 FROM cte
SELECT * FROM tbl_family WHERE familyID NOT IN (SELECT familyID FROM temp1)
UNION ALL
SELECT * FROM temp1
ORDER BY familyID

Related

How to match more than one rows using INNER JOIN with MySQL?

What is the right way to select films which labels are 'Action' AND 'Drama' using INNER JOIN ?
I've tried this query, the result must be 'Taken, The Godfather' but, no result returned.
SELECT
f.film_guid,
f.film_name
FROM
films as f
INNER JOIN
film_labels as l ON l.film_guid = f.film_guid
WHERE
l.label = 'Action' AND l.label = 'Drama'
Table: films
+------------+----------------+
| film_guid | film_name |
+------------+----------------+
| filmguid_1 | Taken |
| filmguid_2 | Matrix |
| filmguid_3 | The Godfather |
+------------+----------------+
Table: film_labels
+------------+----------------+
| film_guid | label |
+------------+----------------+
| filmguid_1 | Action |
| filmguid_1 | Drama |
| filmguid_1 | Family |
| filmguid_2 | Action |
| filmguid_3 | Action |
| filmguid_3 | Drama |
+------------+----------------+
You are looking for a rows in film_labels that contains both Action and Drama, which cannot happen. You need to search across labels that correspond to the given film, which suggest aggregation:
SELECT f.film_guid, f.film_name
FROM films as f
INNER JOIN film_labels as l ON l.film_guid = f.film_guid
WHERE l.label IN ('Action', 'Drama') -- either one, or the other
GROUP BY f.film_guid, f.film_name
HAVING COUNT(*) = 2 -- both match
Note that you could also use exists with correlated subquery. It is a bit longer to type but could be more efficient (with the right indexes in place), since it avoids the need for aggregation:
SELECT f.*
FROM films as f
WHERE
EXISTS (SELECT 1 FROM film_labels l WHERE l.film_guid = f.film_guid AND l.label = 'Action')
AND EXISTS (SELECT 1 FROM film_labels l WHERE l.film_guid = f.film_guid AND l.label = 'Drama')
For performance with the second query, you want an index on film_labels(film_guid , label).

IF using inner join MySql?

How do I build a joint to select either table
Table jobs
Id | name | salary | company | type
1 | php | 17.850 | 5 | 1
2 | mysql | 4.500 | 89 | 2
2 | nodejs | 7.500 | 89 | 1
Table Company
Id | name | Area | status
1 | Facebook| Developer| 1
2 | Google | Manager | 1
Table Candidate
Id | name | City | phone
1 | Alan Kout | Nevada | 1 555 6666
2 | Wagner Mom | L.A. | 1 444 8965
My query mysql, inner join candidate or company
If type == 1 in table jobs INNER JOIN ON table company
If type == 2 in table jobs INNER JOIN ON table candidate
Example
SELECT * FROM table_jobs
IF(table_jobs.type == 1, INNER JOIN table_company, INNER JOIN table_candidate)
This is possible?
You can achieve this using a LEFT JOIN instead of an INNER JOIN:
SELECT *
FROM table_jobs tj
LEFT JOIN table_company tco ON tj.type = 1 AND tco.id = tj.id
LEFT JOIN table_candidate tca ON tj.type = 2 AND tca.id = tj.id
This will join to table_company where the type is 1, and table_candidate where the type is 2.
You can then SELECT whichever columns are needed from each table as appropriate.
Use left join and coalesce():
SELECT tj.*,
coalesce(co.name, ca.name) as name,
. . .
FROM table_jobs tj LEFT JOIN
table_company co
ON co.id = tj.id and tj.type = 1 LEFT JOIN
table_candidate ca
ON ca.id = tj.id and tj.type = 2;
If you want both joins in one result you can use the UNION operator
SELECT *
FROM table_jobs INNER JOIN table_company
WHERE table_jobs.type=1
UNION
SELECT *
FROM table_jobs INNER JOIN table_candidate
WHERE table_jobs.type=2

SQL select from 1 x N where all bigger than

I have tables books and bookType which pose a 1 X n relationship.
books
+-----+------------------+----------+-------+
| id | title | bookType | price |
+-----+------------------+----------+-------+
| 1 | Wizard of Oz | 3 | 14 |
| 2 | Huckleberry Finn | 1 | 16 |
| 3 | Harry Potter | 2 | 25 |
| 4 | Moby Dick | 2 | 11 |
+-----+------------------+----------+-------+
bookTypes
+-----+----------+
| id | name |
+-----+----------+
| 1 | Fiction |
| 2 | Drama |
| 3 | Children |
+-----+----------+
How would I retrieve bookTypes where all books are more expensive than e.g. 12($)?
In this case, the expected output would be:
+-----+----------+
| id | name |
+-----+----------+
| 1 | Fiction |
| 3 | Children |
+-----+----------+
You can use not exists:
select t.*
from bookTypes t
where not exists (
select 1
from books b
where b.bookType = t.id and b.price < 12
)
If you want to select book types that also have at least one associated book:
select t.*
from bookTypes t
where
exists (select 1 from books b where b.bookType = t.id)
and not exists (select 1 from books b where b.bookType = t.id and b.price < 12)
Do a GROUP BY, use HAVING to return only booktypes having the lowest price > 12.
SELECT bt.name
FROM bookTypes bt
INNER JOIN books b ON b.bookType = bt.id
group by bt.name
HAVING SUM(b.price <= 12) = 0;
You can directly consider using having min(price) >= 12 with grouping by bookType
select t.id, t.name
from bookTypes t
join books b
on t.id = b.bookType
group by b.bookType
having min(price) >= 12
Moreover, if your DB's version is at least 10.2, then you can also use some window functions for analytical queries such as min(..) over (partition by .. order by ..) :
with t as
(
select t.id, t.name, min(price) over (partition by bookType) as price
from bookTypes t
join books b
on t.id = b.bookType
)
select id, name
from t
where price >= 12
in which min() over (..) window function determines minimum price for each booktype by use of partition by bookType
Demo
I think GMB's solution is likely the best so far. But for sake of completeness: You can also use the ALL operator with a correlated subquery. That's probably the most straight forward solution.
SELECT *
FROM booktypes bt
WHERE 12 < ALL (SELECT b.price
FROM books b
WHERE b.booktype = bt.id);
Can you not just select from books inner join bookTypes on id WHERE price > 12?
SELECT bt.*
FROM bookTypes bt
INNER JOIN books b ON b.bookType = bt.id
WHERE b.price > 12

MySQL select all left entries from a table which is joined from another table

I have the following MySQL-Statement:
SELECT norm.NormID, norm.NormName
FROM (assignment
INNER JOIN norm
ON assignment.NID = norm.NormID )
INNER JOIN wire
ON assignment.LID = wire.WireID
WHERE wire.WireID= 109
ORDER BY norm.NormName;
Now what I got are the entries from the table assignment with the NormID and NormName for that WireID.
What I want to get are the entries from the table norm, which are not setted for this WireID.
E.g.:
WireID has the norm assignment A, B, D, G.
The table norm has the entries A, B, C, D, E, F, G, H.
What I want to get from the MySQL-Statment are the entries C, E, F, H.
How can I select those left norm entries for this WireID?
With the above statement I would get:
-----------------------
| NormID | NormName |
-----------------------
| 1 | A |
| 2 | B |
| 4 | D |
| 7 | G |
-----------------------
I want to have this Table:
-----------------------
| NormID | NormName |
-----------------------
| 3 | C |
| 5 | E |
| 6 | F |
| 8 | H |
-----------------------
I think (if I understood what you asked) you can try this :
SELECT norm.NormID, norm.NormName
FROM assignment
INNER JOIN norm ON assignment.NID = norm.NormID
LEFT JOIN wire ON assignment.LID = wire.WireID
WHERE assignment.LID= 109
AND wire.wireID IS NULL
ORDER BY norm.NormName;
Edit after your comments.
I think you could use:
SELECT A.NormID, A.NormName
FROM norm A
LEFT JOIN (SELECT NID FROM assignment WHERE LID = 109) B ON B.NID = A.NormID
WHERE B.NID IS NULL
ORDER BY A.NormName;
OR
SELECT A.NormID, A.NormName
FROM norm A
WHERE NOT EXISTS (SELECT 1 FROM assignment WHERE LID = 109 AND ASSIGNMENT.NID = A.NormID)
ORDER BY A.NormName;
Try this:
select norm.NormID,norm.NormName from norm
Inner JOIN
assignment on assignment.NID = norm.NormID
where assignment.LID in(select wireID from Wire where WireID = 109)
Im not so sure coz i dont have your data
after you added a sample data the entries that are not setted with 109 wireid are these:
SELECT norm.NormID, norm.NormName
FROM assignment
inner JOIN norm
ON assignment.NID = norm.NormID
INNER JOIN wire
ON assignment.LID = wire.WireID
WHERE wire.WireID <> 109
ORDER BY norm.NormName;

MySQL JOIN, WHERE and IF issue

i have table of members
table members :
pid| id | name
1 | id01 | jenny
2 | id02 | kain
3 | id03 | alex
and have another table members_opt
table members_opt
pid | members_id | category
1 | id01 | cat
2 | id01 | dog
3 | id02 | dog
4 | id03 | NULL
now i use below SQL query
SELECT * FROM members a JOIN
(SELECT members_id, max(category) as category FROM members_opt GROUP BY members_id) b
ON a.id = b.members_id
But this SQL Query not catch "id03"'s data because "id03"'s members_opt.category is NULL
I want this result
result :
id | name | category
id01 | jenny | cat
id02 | kain | dog
id03 | alex | NULL
(the result now showed double name, double id value.)
How can i use SQL query?
You can try this -
SELECT * FROM members a JOIN
(SELECT members_id, max(CASE WHEN category IS NULL THEN 0) as category FROM members_opt GROUP BY members_id) b
ON a.id = b.members_id
Use the below query to get your desired output -
SELECT a.id, a.name, b.category FROM members a INNER JOIN
(SELECT members_id, category FROM members_opt GROUP BY members_id) b
ON a.id = b.members_id
Just change JOIN to LEFT JOIN :
SELECT a.id, a.name, b.category
FROM members a
LEFT JOIN (
SELECT members_id, max(category) as category
FROM members_opt GROUP BY members_id) b
ON a.id = b.members_id
and if you want 'cat' for 'jenny', you should use aggregation function min.