Count(*) Multiple Tables - mysql

I was browsing for a solution to getting counts from multiple tables and I came across the following answer:
SELECT COUNT(*),(SELECT COUNT(*) FROM table2) FROM table1
It works great, however I can't seem to get it to work for more than just 2 tables. My current code is as follows:
SELECT COUNT(*),
(SELECT COUNT(*) FROM TABLE1),
(SELECT COUNT(*) FROM TABLE2),
(SELECT COUNT(*) FROM TABLE3),
(SELECT COUNT(*) FROM TABLE4),
(SELECT COUNT(*) FROM TABLE5),
(SELECT COUNT(*) FROM TABLE6),
(SELECT COUNT(*) FROM TABLE7),
(SELECT COUNT(*) FROM TABLE8),
(SELECT COUNT(*) FROM TABLE9),
(SELECT COUNT(*) FROM TABLE10),
(SELECT COUNT(*) FROM TABLE11),
(SELECT COUNT(*) FROM TABLE12),
(SELECT COUNT(*) FROM TABLE13),
(SELECT COUNT(*) FROM TABLE14),
(SELECT COUNT(*) FROM TABLE15),
(SELECT COUNT(*) FROM TABLE16),
(SELECT COUNT(*) FROM TABLE17),
(SELECT COUNT(*) FROM TABLE18)
FROM TABLE19
However, it only counts TABLE1 and TABLE19. I need to count all tables (TABLE1-18) as well as TABLE19 (hopefully using a structure to similar to the first example).

Depending on which DB this is it could slightly change...
For Oracle do something like:
Select (select count(*) from table1) as table1Count,
(select count(*) from table2) as table2Count
from dual
If it's SQL Server then just leave off the from dual.
EDIT:
Since you mentioned you are using MySQL in the comments:
Get record counts for all tables in MySQL database

Use aliases so that the columns have unique names:
SELECT
(SELECT COUNT(*) FROM TABLE1) AS count_table1,
(SELECT COUNT(*) FROM TABLE2) AS count_table2,
(SELECT COUNT(*) FROM TABLE3) AS count_table3,
etc..
(SELECT COUNT(*) FROM TABLE19) AS count_table19

If you are happy to accept the results in rows, not columns, you can always use a UNION:
SELECT "table1", COUNT(*) FROM table1 UNION
SELECT "table2", COUNT(*) FROM table2 UNION
SELECT "table3", COUNT(*) FROM table3
etc.

On MySQL this works:
select sum(total) from(
select count(*) as total from Table1
union
select count(*) as total from Table2) as a;

Related

How I create a view with the record count of different tables

I need to create a view with the record count from 3 different tables but with the same columns.
That is, these queries
SELECT COUNT (*) AS count_a from table1 ta
SELECT COUNT (*) AS count_b from table2 tb
SELECT COUNT (*) AS count_c from table3 tc
Introduced in a view, the join can be a solution but I think this would take a lot of work, and likewise being a different count in each table, when using join it would discard some, if in a table there are 100, 200, 15. No I know if the join would be the solution to this.
I wanted to use the GROUP_CONCAT but reviewing a bit does not seem to be a solution, anyway. In simple words I want the count of different tables in a view so that it is displayed
Desired result
count_a
count_b
count_c
100
200
15
Greetings and thanks in advance
PS: right now I do not remember well how the JOIN works
If you want to display only the result of these 3 counts, without any join between tables. I suggest to use a script like this :
create view desired_result
as select * from (
(SELECT COUNT(*) AS count_a from table1) t1,
(SELECT COUNT(*) AS count_b from table2) t2,
(SELECT COUNT(*) AS count_c from table3) t3);
You can try it with this fiddle link
try a query like this:
SELECT
(SELECT count(*) FROM table1) AS count_a,
(SELECT count(*) FROM table2) AS count_b,
(SELECT count(*) FROM table3) AS count_c;
If you like without JOIN:
SELECT
(SELECT count(*) as count_a FROM table1) AS count_a,
(SELECT count(*) as count_b FROM table2) AS count_b,
(SELECT count(*) as count_c FROM table3) AS count_c;
If you like with JOIN:
SELECT ta.count_a, tb.count_b, tc.count_c FROM
(
SELECT COUNT (*) AS count_a, 1 AS rn from table1) ta
JOIN (
SELECT COUNT (*) AS count_b, 1 AS rn from table2) tb
ON ta.rn = tb.rn
JOIN (
SELECT COUNT (*) AS count_c, 1 AS rn from table3) tc
ON ta.rn = tc.rn

MySQL Select works fine but Delete hangs indefinitely based on the position of GROUP BY

select * from table1 where ID in (
select min(a.ID) from (select * from table1) a group by id_x, id_y, col_z having count(*) > 1)
Above query ran in 2.2 seconds returning four result. Now when I change the select * to delete, it hangs up indefinitely.
delete from table1 where ID in (
select min(a.ID) from (select * from table1) a group by id_x, id_y, col_z having count(*) > 1)
If I move the position of group by clause inside the alias select query, it will no longer hang.
delete from table1 where ID in (
select a.ID from (select min(ID) from table1 group by id_x, id_y, col_z having count(*) > 1) a)
Why does it hang? Even though (select * from table1) pulls millions of records, the query doesn't seem to stop executing for hours. Can anybody explain what huddles the query? It puzzles me because the select query works fine whereas the delete query hangs.
EDIT:
My focus here is why it hangs. I already posted work-around that works fine. But in order to develop prevention system, I need to get to the root cause of this..
Use a JOIN instead of WHERE ID IN (SELECT ...).
DELETE t1
FROM table1 AS t1
JOIN (
SELECT MIN(id) AS minId
FROM table1
GROUP BY id_x, id_y, col_z
HAVING COUNT(*) > 1) AS t2
ON t1.id = t2.minId
I think your query is not being optimized because it has to recalculate the subquery after each deletion, since deleting a row could change the MIN(id) for that group. Using a JOIN requires the grouping and aggregation to be done just once.
Try this:
delete t
from table1 t join
(select min(id) as min_id
from table1
group byid_x, id_y, col_z
having count(*) >= 2
) tt
on tt.min_id = t.id;
That said, you probably don't want to delete just the minimum id. I'm guessing you want to keep the most recent id. If so:
delete t
from table1 t left join
(select max(id) as max_id
from table1
group byid_x, id_y, col_z
having count(*) >= 2
) tt
on tt.max_id = t.id
where tt.max_id is null;

Union n amount of tables in mariadb and find the distinct value based on timestamp

I need to make a search on multiple database tables. Since all tables have the same structure(same columns, they are actually archived data for the same table) I can use UNION ALL to combine them all.
However, the challenge is I need to get the distinct value for Username Column and for every Username value I need to find the earliest Time_stamp after using UNION ALL for all archived data.
I went through the documentation and googled for a quite some time and couldn't find a satisfying solution withing MySQL.
The only way I can think of is to write some external code to search the values with UNION ALL for each Username. And run the query 1 by 1. Knowing that I have 100k distinct Username listed in the database, it will take quite some time. I have already started this script, however, it feels really inefficient and waste of resources.
SELECT * FROM table1 WHERE `USERNAME` LIKE 'USERNAMEXXX'
UNION ALL
SELECT * FROM table2 WHERE `USERNAME` LIKE 'USERNAMEXXX'
UNION ALL
SELECT * FROM table3 WHERE `USERNAME` LIKE 'USERNAMEXXX'
UNION ALL
SELECT * FROM table4 WHERE `USERNAME` LIKE 'USERNAMEXXX'
ORDER BY TIME_STAMP ASC
LIMIT 1
Above SQL query gives me what I want for every username but I need to iterate it for each distinct Username.
Just to add, I have another table that holds all the distinct Username values. I use that table to populate the search on my external code solution.
Is there any way to achieve this using native SQL, without external scripting? Something that combines distinct, union all and order by timestamp asc limit 1.
In MySQL 8+, you can use window functions:
SELECT t.*
FROM (SELECT t.*,
ROW_NUMBER() OVER (PARTITION BY username ORDER BY timestamp) as seqnum
FROM ((SELECT t.* FROM table1 t) UNION ALL
(SELECT t.* FROM table2 t) UNION ALL
(SELECT t.* FROM table3 t) UNION ALL
(SELECT t.* FROM table4 t)
) t
) t
WHERE seqnum = 1;
In earlier versions, you can use variables:
SELECT t.*
FROM (SELECT t.*,
(#rn := IF(#u = username, #rn + 1,
IF(#u := username, 1, 1)
)
) as seqnum
ROW_NUMBER() OVER (PARTITION BY username ORDER BY timestamp) as seqnum
FROM ((SELECT t.* FROM table1 t) UNION ALL
(SELECT t.* FROM table2 t) UNION ALL
(SELECT t.* FROM table3 t) UNION ALL
(SELECT t.* FROM table4 t)
ORDER BY username, timestamp
) t CROSS JOIN
(SELECT #u := '', #rn := 0) params
) t
WHERE seqnum = 1;
The above assume that you want all the columns. If you just want the minimum timestamp and username, use aggregation:
SELECT username, MIN(timestamp),
FROM ((SELECT t.* FROM table1 t) UNION ALL
(SELECT t.* FROM table2 t) UNION ALL
(SELECT t.* FROM table3 t) UNION ALL
(SELECT t.* FROM table4 t)
) t
GROUP BY username;
If I understand right your question
You could use min for earliest timestam min(my_time_stamp) and UNION (for return distinct )
select username, min(my_time_stamp)
from table1
group by username
union
select username, min(my_time_stamp)
from table2
group by username
union
select username, min(my_time_stamp)
from table3
group by username
....
union
select username, min(my_time_stamp)
from tableN
group by username
order by username

How to get AS two separate selectors with UNION?

As sadly SQL is my weakest skill.
I'm trying to use UNION in a VIEW, where I can get statistics from two different tables with one query.
SELECT COUNT(*) AS `customer_count` FROM `Customers`
UNION
SELECT COUNT(*) AS `supplier_count` FROM `Suppliers`;
[Demo table]
However, it only returns customer_count, with two rows. Is there anyway, to make this work, so it returns customer_count and supplier_count separately?
You would need a cross join to see the results adjacent to each other in one row. So you would select from both the tables without a join condition.
select * from
(select count(*) as customer_count from Customers) x,
(select count(*) as supplier_count from Suppliers) y
select
(SELECT COUNT(*) FROM Customers) as customer_count,
(SELECT COUNT(*) FROM Suppliers) AS supplier_count
Using your Table Demo.
The key is use alias so the field names match on each union select.
In this case TableSource and Total
SELECT 'Customer' as TableSource, Count(City) as Total FROM Customers
UNION
SELECT 'Suppliers' as TableSource, Count(City) as Total FROM Suppliers;
CREATE VIEW `vw_count` AS
select (select count(0) from `tbl`) AS `customer_count`,
(select count(0) from `tbl2`) AS `supplier_count`;

How to show the difference between two counts using aliases?

I'm creating custom views that show totals for different things in a database, and I'd like to also show the differences.
For example;
SELECT
(SELECT COUNT(*) FROM `documents`) AS `doc_count`,
(SELECT COUNT(*) FROM `contacts`) AS `user_count`,
(`doc_count` - `user_count`) AS `difference`;
I get an error using the aliases this way. Is there a way to write this query without repeating select count(*) queries?
You could wrap both queries with an additional query:
SELECT doc_count, user_count, doc_count - user_count AS difference
FROM ((SELECT COUNT(*) FROM `documents`) AS doc_count,
(SELECT COUNT(*) FROM `contacts`) AS user_count) t
No you can't use the aliases at same level of query you have to use the whole expression or use sub select
SELECT
(SELECT COUNT(*) FROM `documents`) AS `doc_count`,
(SELECT COUNT(*) FROM `contacts`) AS `user_count`,
((SELECT COUNT(*) FROM `documents`) - (SELECT COUNT(*) FROM `contacts`)) AS `difference`;
Here is a "workaround" to get the result you're looking for:
SELECT C.doc_count
,C.user_count
,C.doc_count - C.user_count AS `difference`
FROM (SELECT
(SELECT COUNT(*) FROM `documents`) AS `doc_count`
,(SELECT COUNT(*) FROM `contacts`) AS `user_count`) C
But i'm not sure about the performance of such kind of query...
Hope this will help you
I would move these to the from clause and use cross join:
SELECT d.doc_count, u.user_count, (d.doc_count - u.user_count) as difference
FROM (SELECT COUNT(*) as doc_count FROM `documents`) d CROSS JOIN
(SELECT COUNT(*) as user_count FROM `contacts`) u;