SQL - How to use COUNT() with more than one table - mysql

I have a table that includes an COUNT() function that returns all the fields and counts them out. Now, I need to join another table and return both the number of rows that match and the rows that do no match. I am not exactly sure how I can use the GROUP BY and COUNT() function and get the results I need.
SELECT tb1.type, count(tb1.type) as total
FROM table1 tb1
GROUP BY tb1.type
That will return the totals for me:
RESULTS for TABLE1:
Type total
Egg 200
Cream 133
Milk 12
That's great. However, now I have another table...table 2 that has some records that match so doing that same query on that table will show something like:
RESULTS for TABLE2:
Type total
Egg 187
Cream 103
Milk 6
So, my results will need to look like this:
RESULTS for TABLE1:
Type total totalINTABLE2 totalNOTINTABLE2
Egg 200 187 13
Cream 133 103 30
Milk 12 6 6
Not sure if joining is best here, or a UNION, or EXISTS. What's getting me confused is the group by.

Well, you can simply JOIN both the resultset like
SELECT tb1.type,
tb1.total,
tb2.totalINTABLE2,
(tb1.total - tb2.totalINTABLE2) as totalNOTINTABLE2
FROM (
SELECT type, count(type) as total
FROM table1
GROUP BY type) tb1
JOIN (
SELECT type, count(type) as totalINTABLE2
FROM table2
GROUP BY type) tb2 ON tb1.type = tb2.type;

Use a CTE to calculate the total in each table then join them together:
;WITH
cte1 as (
SELECT tb1.type, count(tb1.type) as total
FROM table1 tb1
GROUP BY tb1.type
),
cte2 as (
SELECT tb2.type, count(tb2.type) as total
FROM table2 tb2
GROUP BY tb2.type
)
SELECT ISNULL(cte1.type, cte2.type) as type,
cte1.total as TotalInTable1,
cte2.total as TotalInTable2,
ISNULL(cte1.total, 0) - ISNULL(cte2.total, 0) as TotalNotInTable2
FROM cte1
FULL JOIN cte2 ON cte1.type = cte2.type

Related

MySQL max value in row

I am facing a problem with MySQL query which is a variant of "Id for row with max value". I am either getting error or incorrect result for all my trials.
Here is the table structure
Row_id
Group_id
Grp_col1
Grp_col2
Field_for_aggregate_func
Another_field_for_row
For all rows with a particular group_id, I want to group by fields Grp_col1, Grp_col2 then get max value of Field_for_aggregate_func and then corresponding value of Another_field_for_row.
Query I have tried is like below
SELECT c.*
FROM mytable as c left outer join mytable as c1
on (
c.group_id=c1.group_id and
c.Grp_col1 = c1.Grp_col1 and
c.Grp_col2 = c1.Grp_col2 and
c.Field_for_aggregate_func > c1.Field_for_aggregate_func
)
where c.group_id=2
Among alternative solutions for this problem I want a high performance solution as this will be used for large set of data.
EDIT: Here is the sample set of row and expected answer
Group_ID Grp_col1 Grp_col2 Field_for_aggregate_func Another_field_for_row
2 -- N 12/31/2015 35
2 -- N 1/31/2016 15 select 15 from group for max value 1/31/2016
2 -- Y 12/31/2015 5
2 -- Y 1/1/2016 15
2 -- Y 1/2/2016 25
2 -- Y 1/3/2016 30 select 30 from group for max value 1/3/2016
You can use a sub-query to find the maximums, then join that with the original table, along the lines of:
select m1.group_id, m1.grp_col1, m1.grp_col2, m1.another_field_for_row, max_value
from mytable m1, (
select group_id, grp_col1, grp_col2, max(field_for_aggregate_func) as max_value
from mytable
group by group_id, grp_col1, grp_col2) as m2
where m1.group_id=m2.group_id
and m1.grp_col1=m2.grp_col1
and m1.grp_col2=m2.grp_col2
and m1.field_for_aggregate_func=m2.max_value;
Watch out for when there is more than one max_value for the given grouping. You'll get multiple rows for that grouping. Fiddle here.
Try this.
See Fiddle demo here
http://sqlfiddle.com/#!9/9a3c26/8
Select t1.* from table1 t1 inner join
(
Select a.group_id,a.grp_col2,
A.Field_for_aggregate_func,
count(*) as rnum from table1 a
Inner join table1 b
On a.group_id=b.group_id
And a.grp_col2=b.grp_col2
And a.Field_for_aggregate_func
<=b.Field_for_aggregate_func
Group by a.group_id,
a.grp_col2,
a.Field_for_aggregate_func) t2
On t1.group_id=t2.group_id
And t1.grp_col2=t2.grp_col2
And t1.Field_for_aggregate_func
=t2.Field_for_aggregate_func
And t2.rnum=1
Here first I am assigning a rownumber in descending order based on date. The selecting all the records for that date.

Mysql union select error

I am trying to display all records from table1 even if the catid not existing in table2 (all employee in table2 should have all catid from table1 with 0 days if not exising in table2) with the following sql query but getting an error
Error Code: 1054. Unknown column 'catid' in 'group statement'
select empid,days from table2 union select catid from
table1 group by empid, catid;
table1:
catid
1
2
3
table2:
empid catid days (computed column count(*))
1000 1 8
1000 3 10
expected result:
empid catid days
1000 1 8
1000 2 0 <---catid 2 and days 0 if catid not existing in table2 for empid 1000
1000 3 10
That is not the function of the union statement. Union statement does a set like capability which merging two sets. What you are looking for a is a join with the table 1 where you do a count and group by catid. Your data model to achieve this output itself is grievously wrong ;)
select employeeid, catid, sum(days) from table1, table2 group by employeeid, catid;
You just need a LEFT JOIN:
Select tab2.empid, tab2.catid, ifnull(tab2.days, 0)
from tab2
left join tab1 on tab2.catid = tab1.catid
Please note : While doing a UNION the number and type of the columns present in the first select should be the same as the next Selects.
So you need to first make the select columns in sync first.
can you check this and add empid similarly.
SELECT TABLE1.CATID, IFNULL(TABLE2.DAYS,0) FROM table1 LEFT OUTER JOIN
table2 ON table1.catid = table2.catid
Please use LEFT JOIN with IFNULL.
Select table2.empid, table1.catid, IFNULL(table2.days, 0) from table2
LEFT JOIN table1 ON table2.catid = table1.catid;

Create a count in count in sql select

I have a question: it's possible to create an count in count in sql:
my code is:
SELECT COUNT( DISTINCT p.id_participant ) as number
FROM participation p
INNER JOIN message m ON m.id_participation=p.id
AND p.id_event = 4
I want to add in first count another count from table winners with count (id_winner)
Help me please, Exist a solution?
You need to use the aggregate function SUM.
For example,
SQL> SELECT SUM(val)
2 FROM (SELECT Count(*) VAL
3 FROM emp
4 UNION
5 SELECT Count(*) VAL
6 FROM dept);
SUM(VAL)
----------
18

Count duplicates records in Mysql table?

I have table with, folowing structure.
tbl
id name
1 AAA
2 BBB
3 BBB
4 BBB
5 AAA
6 CCC
select count(name) c from tbl
group by name having c >1
The query returning this result:
AAA(2) duplicate
BBB(3) duplicate
CCC(1) not duplicate
The names who are duplicates as AAA and BBB. The final result, who I want is count of this duplicate records.
Result should be like this:
Total duplicate products (2)
The approach is to have a nested query that has one line per duplicate, and an outer query returning just the count of the results of the inner query.
SELECT count(*) AS duplicate_count
FROM (
SELECT name FROM tbl
GROUP BY name HAVING COUNT(name) > 1
) AS t
Use IF statement to get your desired output:
SELECT name, COUNT(*) AS times, IF (COUNT(*)>1,"duplicated", "not duplicated") AS duplicated FROM <MY_TABLE> GROUP BY name
Output:
AAA 2 duplicated
BBB 3 duplicated
CCC 1 not duplicated
For List:
SELECT COUNT(`name`) AS adet, name
FROM `tbl` WHERE `status`=1 GROUP BY `name`
ORDER BY `adet` DESC
For Total Count:
SELECT COUNT(*) AS Total
FROM (SELECT COUNT(name) AS cou FROM tbl GROUP BY name HAVING cou>1 ) AS virtual_tbl
// Total: 5
why not just wrap this in a sub-query:
SELECT Count(*) TotalDups
FROM
(
select Name, Count(*)
from yourTable
group by name
having Count(*) > 1
) x
See SQL Fiddle with Demo
The accepted answer counts the number of rows that have duplicates, not the amount of duplicates. If you want to count the actual number of duplicates, use this:
SELECT COALESCE(SUM(rows) - count(1), 0) as dupes FROM(
SELECT COUNT(1) as rows
FROM `yourtable`
GROUP BY `name`
HAVING rows > 1
) x
What this does is total the duplicates in the group by, but then subtracts the amount of records that have duplicates. The reason is the group by total is not all duplicates, one record of each of those groupings is the unique row.
Fiddle: http://sqlfiddle.com/#!2/29639a/3
SQL code is:
SELECT VERSION_ID, PROJECT_ID, VERSION_NO, COUNT(VERSION_NO) AS dup_cnt
FROM MOVEMENTS
GROUP BY VERSION_NO
HAVING (dup_cnt > 1 && PROJECT_ID = 11660)
I'm using this query for my own table in PHP, but it only gives me one result whereas I'd like to the amount of duplicate per username, is that possible?
SELECT count(*) AS duplicate_count
FROM (
SELECT username FROM login_history
GROUP BY username HAVING COUNT(time) > 1
) AS t;

Using SUM() without grouping the results

I already read (this), but couldn't figure out a way to implement it to my specific problem. I know SUM() is an aggregate function and it doesn't make sense not to use it as such, but in this specific case, I have to SUM() all of the results while maintaining every single row.
Here's the table:
--ID-- --amount--
1 23
2 11
3 8
4 7
I need to SUM() the amount, but keep every record, so the output should be like:
--ID-- --amount--
1 49
2 49
3 49
4 49
I had this query, but it only sums each row, not all results together:
SELECT
a.id,
SUM(b.amount)
FROM table1 as a
JOIN table1 as b ON a.id = b.id
GROUP BY id
Without the SUM() it would only return one single row, but I need to maintain all ID's...
Note: Yes this is a pretty basic example and I could use php to do this here,but obviously the table is bigger and has more rows and columns, but that's not the point.
SELECT a.id, b.amount
FROM table1 a
CROSS JOIN
(
SELECT SUM(amount) amount FROM table1
) b
You need to perform a cartesian join of the value of the sum of every row in the table to each id. Since there is only one result of the subselect (49), it basically just gets tacked onto each id.
With MS SQL you can use OVER()
select id, SUM(amount) OVER()
from table1;
select id, SUM(amount) OVER()
from (
select 1 as id, 23 as amount
union all
select 2 as id, 11 as amount
union all
select 3 as id, 8 as amount
union all
select 4 as id, 7 as amount
) A
--- OVER PARTITION ID
PARTITION BY which is very useful when you want to do SUM() per MONTH for example or do quarterly reports sales or yearly...
(Note needs distinct it is doing for all rows)
select distinct id, SUM(amount) OVER(PARTITION BY id) as [SUM_forPARTITION]
from (
select 1 as id, 23 as amount
union all
select 1 as id, 23 as amount
union all
select 2 as id, 11 as amount
union all
select 2 as id, 11 as amount
union all
select 3 as id, 8 as amount
union all
select 4 as id, 7 as amount
) OverPARTITIONID
Join the original table to the sum with a subquery:
SELECT * FROM table1, (SELECT SUM(amount) FROM table1 AS amount) t
This does just one sum() query, so it should perform OK:
SELECT a.id, b.amount
FROM table1 a
cross join (SELECT SUM(amount) as amount FROM table1 AS amount) b
in case someone else has the same problem and without joining we can do the following
select *
,totcalaccepted=(select sum(s.acceptedamount) from cteresult s)
, totalpay=(select sum(s.payvalue) from cteresult s)
from cteresult t
end
Using Full Join -
case when you need sum of amount field from tableB and all data from tableA on behalf of id match.
SELECT a.amount, b.* FROM tableB b
full join (
select id ,SUM(amount) as amount FROM tableA
where id = '1' group by id
) a
on a.id = b.id where a.id ='1' or b.id = '1' limit 1;