MySQL select count based on two rows data - mysql

Table column headers: n,t1,t2
entries :
1 A B
2 A C
3 B C
4 D E
5 B A
How do I count total number of rows each letter appears in t1 MINUS the number of rows they appear in t2 ? I need to do something like following 2 lines in 1 query :
select count(*) as val,t1 from table group by t1
select count(*) as val,t2 from table group by t2
Thanks,
Martin

Here is one way:
select t1, max(t1cnt) - max(t2cnt) as diff
from ((select t1, count(*) as t1cnt, 0 as t2cnt
from t
group by t1
) union all
(select t2, 0 as t1cnt, count(*) as t2cnt
from t
group by t2
)
) t
group by t1
Using the union all ensures that you get all possible values from both columns, even values that only appear in one column.

You can use the following query to get the result. This query first gets a list of all the distinct t1 and t2 values (this is the UNION query). Once you have the list of these values, then you can use a LEFT JOIN to the original queries that you posted:
select d.col, coalesce(totT1, 0) - coalesce(totT2, 0) Total
from
(
select t1 col
from entries
union
select t2 col
from entries
) d
left join
(
select count(*) totT1, t1
from entries
group by t1
) d1
on d.col = d1.t1
left join
(
select count(*) totT2, t2
from entries
group by t2
) d2
on d.col = d2.t2;
See SQL Fiddle with Demo

Related

Wrong count result in mysql when joining two tables

I am trying to join two tables and get the count and grouped by specific field. However, it outputs same count values even if the other table consist only two rows. How should I fix this?
Here's my code:
SELECT tbl1.preferredDay, COUNT(tbl1.preferredDay) as count_1, COUNT(tbl2.preferredDay) as count_2
FROM tblschedule as tbl1
LEFT JOIN tblappointments as tbl2 ON (tbl1.preferredDay = tbl2.preferredDay)
WHERE tbl1.preferredDay = tbl2.preferredDay
GROUP BY preferredDay;
Here is the output but it should be [15, 0][3, 3]
Your query is based on left join it will return the same count().
This is a working query for Mysql 8:
with tbl1 as (
SELECT preferredDay, count(1) as count_1
FROM tblschedule
GROUP BY preferredDay
),
tbl2 as (
SELECT preferredDay, count(1) as count_2
FROM tblappointments
GROUP BY preferredDay
)
select t1.preferredDay, t1.count_1, t2.count_2
from tbl1 t1
inner join tbl2 t2 on t1.preferredDay = t2.preferredDay
There are two WITHs to get separately the count and then an INNER JOIN to join those results
For Mysql 5.7 and lower :
select t1.preferredDay, t1.count_1, t2.count_2
from (
SELECT preferredDay, count(1) as count_1
FROM tblschedule
GROUP BY preferredDay
) as t1
inner join (
SELECT preferredDay, count(1) as count_2
FROM tblappointments
GROUP BY preferredDay
) as t2 on t1.preferredDay = t2.preferredDay

How to remove duplicates from table using join in mysql

select *
from
(
SELECT id, imei1, status
FROM `owarranty_imei` mto
WHERE EXISTS
(
SELECT 1
FROM `owarranty_imei` mti
WHERE mto.imei1=mti.imei1
LIMIT 1, 1
)
) t1
left join `owarranty_warranty_activations` as t2 on t1.id=t2.imei_id
where t2.id is null
limit 100
this is my query. In owarranty_imei has more than 100000 records. i want to get duplicates from imei table which owarranty_imei not in owarranty_warranty_activation table. This query work for few records but when i run it for more than 1000000 records its not working
SELECT
mto.id,
mto.imei1,
mto.status
FROM
`owarranty_imei` mto
INNER JOIN
`owarranty_imei` mti ON mto.imei1=mti.imei1
LEFT JOIN
`owarranty_warranty_activations` as t2 ON mto.id=t2.imei_id
GROUP BY mto.id
HAVING COUNT(t2.id)=0

MySQL select a set of columns with more than one variation in another column

table looks like this:
id group name
1 1 A
2 1 A
3 2 A
4 2 B
5 3 A
I want to select the rows with more than one distinct names in the same group. The result should be the following:
id group name
3 2 A
4 2 B
Any idea how do achieve this?
You can get the groups with aggregation:
select group
from t
group by group
having min(name) <> max(name);
You can get the original rows using join, in, or exists:
select t.*
from t
where t.group in (select group
from t
group by group
having min(name) <> max(name)
);
Note: group is a lousy name for a column because it is a SQL keyword and a MySQL reserved word.
You could do it with a correlated subquery:
SELECT t1.id, t1.group, t1.name
FROM mytable AS t1
WHERE EXISTS (
SELECT * FROM mytable t2
WHERE t2.group=t1.group AND t2.name <> t1.name
);
Or you could do it by counting distinct names in the group:
SELECT t1.id, t1.group, t2.name
FROM mytable AS t1
INNER JOIN (
SELECT t2.group FROM mytable AS t2
GROUP BY t2.group HAVING COUNT(DISTINCT t2.name) > 1
) AS t2 USING (group);

Joining three tables such that extra matches are discarded?

How can I write a query to give the results of three tables such that there's only one result per "line"?
The tables are:
T1 (ID, name, IP)
T2 (ID, date_joined)
T3 (ID, address, date_modified)
The relations are:
T1-T2 1:1, T1-T3 1:M - there can be many address rows per ID in T3.
What I want is a listing of all users with the fields above, but IF they have an address, I only want to record ONE (bonus would be if it is the latest one based on T3.date_modified).
So I should end up with exactly the number of records in T1 (happens to be equal to T2 in this case) and no more.
I tried:
select t.ID, t.name, t.IP, tt.ID, tt.date_joined, ttt.ID, ttt.address
from T1 t JOIN T2 tt ON (t.ID = tt.ID) JOIN T3 ttt ON (t.ID = ttt.ID)
And every sensible combination of LEFT, RIGHT, INNER, etc joins I could think of! I keep getting multiple duplicate because of T3
This query should work:
select
t1.ID, t1.name, t1.IP, t2.date_joined, t3x.address
from t1
join t2 on t1.ID = t2.id
left join (
select t3.*
from t3
join (
select id, max(date_modified) max_date
from t3
group by id
) max_t3 on t3.id = max_t3.id and t3.date_modified = max_t3.max_date
) t3x on t1.ID = t3x.id
First you do the normal join between t1 and t2 and then you left join with a derived table (t3x) that is the set of t3 rows having the latest date.
So T2 is actually not relevant here. You just need a way to join from T1 to T3 in a way that gets you at most one T3 row per T1 row.
One way of doing this would be:
select
T1.*,
(select address from T3 where T3.ID=T1.ID order by date_modified desc limit 1)
from T1;
This won't likely be very efficient, being a correlated subquery, but you may not care depending on the size of your dataset.
It's also only good for getting one column from T3, so if you had Address, City, and State, you'd have to figure out something else.
You can use sub query with Top 1 so that u get only one result from T3
here is a sample sql
select * into #T1 from(
select 1 ID
union select 2
union select 3) A
select * into #T2 from(
select 1 ID
union select 2
union select 3) A
select * into #T3 from(
select 1 ID, 'ABC' Address, getDate() dateModified
union select 1, 'DEF', getDate()
union select 3, 'GHI', getDate()) A
select *, (select top 1 Address from #T3 T3 where T3.ID= T1.ID order by datemodified desc) from #T1 T1
inner join #T2 T2 on T1.ID = T2.ID
Bonus :- you can also add order by dateModified desc to get the latest address

Complex MySQL query using group by

Lets say i have two tables
Table1:
product_id, design1, design2
1 A C
2 B A
Table2:
product_id, value
1 10
2 10
Now i want to to sum all the value for particular design for all products.
SELECT designA, SUM(value) FROM (
SELECT b.design1 AS designA, SUM(value) AS value FROM table2 AS a LEFT JOIN table1 AS b ON a.product_id = b.product_id GROUP BY b.design1) AS T GROUP BY designA
It gives me this:
designA SUM(value)
A 10
B 10
Now the problem is that if user has specified design2 in table1 then what ever is the value of design1 will automatically be added in design2. If design2 is not present design1 column then it will be a new row of result:
Desited result is this:
designA SUM(value)
A 20
B 10
C 10
select y.designA, sum(value) from
(select a.design1 as designA, value from
Table1 as a
inner join Table2 as b
on
a.product_id = b.product_id
union all
select a.design2 as designA, value from
Table1 as a
inner join Table2 as b
on
a.product_id = b.product_id) as y
group by y.designA
seems to work for your test data, not tried on other configurations but you should be able to tweak it, if you understand what it's doing.
UNION in the matches based on design2:
SELECT designA, SUM(value) FROM (
SELECT b.design1 AS designA, SUM(value) AS value FROM table2 AS a LEFT JOIN table1 AS b ON a.product_id = b.product_id GROUP BY b.design1
UNION
SELECT b.design2 AS designA, SUM(value) AS value FROM table2 AS a LEFT JOIN table1 AS b ON a.product_id = b.product_id GROUP BY b.design2
) AS T GROUP BY designA