SUM of COUNTS MySQL - mysql

I have the following sql query
SELECT
(SELECT count(cid) from A where uid=45 group by cid) as cats
(SELECT count(cid) from A where uid=45) as cats_total
The first sub-select produces 4 rows and counts the number of items in each cid. The second sub-select produces only 1 row and counts the numbers of items total.
My problem lies in the second sub-select. SQL is producing an error because they have different amounts of rows. Is there an adjustment I can make so the second sub-select has 4 rows, or to whatever amount of rows the first sub-select produces?
UPDATE: Let me clarify further with a table I need to produce
+------+------------+
| cats | cats_total |
+------+------------+
| 2 | 17 |
| 5 | 17 |
| 1 | 17 |
| 9 | 17 |
+------+------------+

Alternative, you can use UNION ALL,
SELECT SUM(totals) grandTotal
FROM
(
SELECT count(cid) totals from A where uid=45 group by cid
UNION ALL
SELECT count(cid) totals from A where uid=45
) s

Kaf is right.
If someone's interested here is a working version tested via jdbc to an Oracle db:
SELECT cats,cats_total from
(SELECT count(cid) as cats from A where uid=45 group by cid)
cross join
(SELECT count(cid) as cats_total from A where uid=45)

you can try
SELECT cats.total, cats_total.total from
(SELECT count(cid) as total from A where uid=45 group by cid) as cats ,
(SELECT count(cid) as total from A where uid=45) as cats_total

I think you can do a cross join of two sub queries;
SELECT cats, cats_total
FROM (SELECT count(cid) as cats from A where uid=45 group by cid) as c1
CROSS JOIN
(SELECT count(cid) as cats_total from A where uid=45) as c2

Related

Multiple similar SELECTs in one statement

I have these SELECT statements in SQL:
this:
SELECT
*
FROM
products
WHERE
product_category = '12'
LIMIT
3
and this:
SELECT
*
FROM
products
WHERE
product_category = '36'
LIMIT
3
and this:
SELECT
*
FROM
products
WHERE
product_id IN ('3178','3181','7403')
LIMIT
3
As you can see they are very similar, what I want is to run these 3 statement effectively, point of that is that whole result should be 9 rows long (because 3x3), and firstly should be displayed three products from category 12, then second three products should be displayed from category 36 and the last three products should be products with IDs 3178,3181,7403.
I know that I can use UNION like this:
(SELECT
*
FROM
products
WHERE
product_category = '12'
LIMIT
3)
UNION
(SELECT
*
FROM
products
WHERE
product_category = '36'
LIMIT
3)
UNION
(SELECT
*
FROM
products
WHERE
product_id IN ('3178','3181','7403')
LIMIT
3)
LIMIT 9
But I wonder, if there is more effective way, because these statements are mostly copies.
for mysql version 8.0 or greater and you can reduce one subquery
select col1,col2,col3 from (SELECT *, row_number() over(partition by product_category order by product_category) rn
FROM
products
WHERE
product_category in( 12,36)
) a where a.rn<=3
union
SELECT
col1,col2,col3
FROM
products
WHERE
product_id IN ('3178','3181','7403') order by product_id
LIMIT
3
In MySQL 8+, you can use window functions:
SELECT p.*
FROM (SELECT p.*,
ROW_NUMBER() OVER (PARTITION BY product_category,
product_id IN (3178, 3181, 7403)
ORDER BY product_category
) as seqnum
FROM products p
WHERE product_category IN (12, 36) OR
product_id IN (3178, 3181, 7403)
) p
WHERE seqnum <= 3
ORDER BY (product_category = 12) DESC,
(product_category = 36) DESC;
UNION ALL
UNION alone will try to merge similar records as one. With UNION ALL, will show everything.
Perhaps if you really want to make this more efficient you have to look at it a slightly different way and not limit yourself to the query, but think about updating your database structure.
For example, you could add a new column in products table to handle the group order.
| product_id | product_category | product_group |
|------------|------------------|---------------|
| 3178 | | c |
| | 12 | a |
| | 36 | b |
| 3181 | | c |
| 7403 | | c |
And get the result with a simple query :
SELECT * from products
WHERE product_group in (a, b, c)
ORDER BY product_group
Depending on why you're limiting to just 3 results per group, it probably makes more sense anyway to use code to manage which of the entries you want to return (so having some code to set the product_group field), not simply the 3 first entries from your table, in no specific order.

Check if ID from a table exists in another table, and if so, how many times

Let's say I have two tables
Table a
some_ID
1
2
3
4
Table b
some_ID
1
2
1
4
Now what I would like to receive is a table like
id amount
1 | 2
2 | 1
I tried with a following query:
SELECT COUNT(a.some_id) as id
FROM Table_a
INNER JOIN Table_b
ON Table_a.some_id = Table.b.some_id
but that only returned how many id rows there are in both tables.
Any help?
Do the grouping on table_b and then join that result set on table_a
SELECT b.* FROM
(
SELECT id, COUNT(*) AS Cnt
FROM Table_b
GROUP BY id
) b
INNER JOIN Table_a a ON a.id = b.id
SQLFiddle
If you want the zero counts:
SELECT a.some_id AS id, count(b.some_id) as amount
FROM a LEFT JOIN b ON a.some_id = b.some_id
GROUP BY a.some_id
Result:
id | amount
1 | 2
2 | 1
3 | 0
4 | 1
If not:
SELECT a.some_id AS id, count(*) as amount
FROM a INNER JOIN b ON a.some_id = b.some_id
GROUP BY a.some_id
Result:
id | amount
1 | 2
2 | 1
4 | 1
The difference is the join type. Once left outer join. Then inner join. Note that in the first case it is important to count with count(b.some_id). With count(*) the rows with missing b entries would be counted as 1. count(*) counts the rows. count(expression) counts the non-null values.
If I understand correctly, you want a histogram of histograms:
select cnt, count(*) as num_ids
from (select id, count(*) as cnt
from b
group by id
) b
group by cnt;

Find the same sets of pairs

I have such scheme in mysql:
TableA (id integer PK, pid integer, mid integer)
Ex. data:
id | pid | mid
1 | 2 | 2
2 | 2 | 4
3 | 3 | 4
4 | 4 | 2
5 | 4 | 4
6 | 3 | 2
7 | 3 | 5
I have pid with some mid's and want to find all pid's with the same set of mid's. In example for pid=2 answer is 2,4
group_concat is not suitable for me
I think it should be simple, but the answer eludes me
UPD:
I have tried group_concat:
SELECT DISTINCT(b.pid) FROM (SELECT pid, group_concat(mid) as concated FROM TableA where pid=100293) as a, (select pid, group_concat(mid) as concated, COUNT(1) as count FROM TableA group by pid) as b where a.concated=b.concated;
Since you are working with integers, instead of group_concat you could generate a bitmask on distinct mid values for each pid and join on that. Then it's just math all the way down:
SELECT DISTINCT pid
FROM (SELECT pid, sum(pow(2,mid)) as midmask FROM (SELECT distinct pid, mid FROM tableA) as t1a GROUP BY pid) as t1
INNER JOIN (SELECT pid, sum(pow(2,mid)) as midmask FROM (SELECT distinct pid, mid FROM tableA) as t2a GROUP BY pid) as t2
ON t1.midmask = t2.midmask
IF mid is already distinct for each pid then you can get rid of the inner-inner subqueries.
Using #GordonLinoff's excellent single-subquery approach where GROUP_CONCAT is only used on the main query (where it won't be so expensive). Instead of the group_concat on the inner query we use the bitmask approach that may be quicker.
SELECT midmask>>1, group_concat(pid)
FROM (SELECT pid, sum(pow(2,mid)) as midmask FROM (SELECT distinct pid, mid FROM tableA) as t1a GROUP BY pid) as t1
GROUP BY midmask;
Results:
+---------+-------------------+
| midmask | group_concat(pid) |
+---------+-------------------+
| 10 | 2,4 |
| 26 | 3 |
+---------+-------------------+
Obviously that midmask in the result set isn't super necessary, but you can pick out the values from the bitmask if you want to see the mid values that contributed to the match if you like.
I'm using the bit right-shift operator to insure that the proper bit is set in the midmask result otherwise you'll be off by one. If you don't care about the output of the midmask, then don't bother with the >>1 portion of the query.
You can use this query. It will give you comma separated pids.
select `mid`, group_concat(`pid`) from `tableA` group by `mid`;
In MySQL, I would approach this using group_concat():
select mids, group_concat(pid)
from (select pid, group_concat(mid order by mid) as mids
from t
group by pid
) t
group by mids;
This solves the general problem, for all pids. Solving for 1 pid is a bit tricky in MySQL (no window functions), but you can try:
select t.pid, t2.pid, count(*)
from t join
t t2
on t.mid = t2.mid and t2.pid = 2
group by t.pid, t2.pid
having count(*) = (select count(*) from t where t.pid = t.pid) and
count(*) = (select count(*) from t where t.pid = t2.pid);
For this, you want indexes on t(mid, pid) and t(pid).

MySql select next lower number without using limit

Is it possible to select the next lower number from a table without using limit.
Eg: If my table had 10, 3, 2 , 1 I'm trying to select * from table where col > 10.
The result I'm expecting is 3. I know I can use limit 1, but can it be done without that?
Try
SELECT MAX(no) no
FROM table1
WHERE no < 10
Output:
| NO |
------
| 3 |
SQLFiddle
Try this query
SELECT
*
FROM
(SELECT
#rid:=#rid+1 as rId,
a.*
FROM
tbl a
JOIN
(SELECT #rid:=0) b
ORDER BY
id DESC)tmp
WHERE rId=2;
SQL FIDDLE:
| RID | ID | TYPE | DETAILS |
------------------------------------
| 2 | 28 | Twitter | #sqlfiddle5 |
Another approach
select a.* from supportContacts a inner join
(select max(id) as id
from supportContacts
where
id in (select id from supportContacts where id not in
(select max(id) from supportContacts)))b
on a.id=b.id
SQL FIDDLE:
| ID | TYPE | DETAILS |
------------------------------
| 28 | Twitter | #sqlfiddle5 |
Alternatively, this query will always get the second highest number based on the inner where clause.
SELECT *
FROM
(
SELECT t.col,
(
SELECT COUNT(distinct t2.col)
FROM tableName t2
WHERE t2.col >= t.col
) as rank
FROM tablename t
WHERE col <= 10
) xx
WHERE rank = 2 -- <<== means second highest
SQLFiddle Demo
SQLFiddle Demo (supports duplicate values)
If you want to get next lower number from table
you can get it with this query:
SELECT distinct col FROM table1 a
WHERE 2 = (SELECT count(DISTINCT(b.col)) FROM table1 b WHERE a.col >= b.col);
later again if you want to get third lower number you can just pass 3 in place of 2 in where clause
again if you want to get second higher number, just change the condition of where clause in inner query with
a.col <= b.col

Counting unique numbers in a column MySQL

I have a query that returns data in the following format:
id | name | number
1 John 12545
1 John 50496
2 Mary 23443
3 Mark 54
3 Mark 5600
3 Mark 50206
I would like to find out the number of distinct ids that appear in the result set. For example, for the result above. I would like to obtain the value 3.
Is there any way to add a column so the result looks like this instead?
count | id | name | number
3 1 John 12545
3 1 John 50496
3 2 Mary 23443
3 3 Mark 54
3 3 Mark 5600
3 3 Mark 50206
My query is:
SELECT * FROM (
SELECT id FROM tableA
WHERE xyz
) as t1
JOIN tableB using (id)
SELECT (SELECT COUNT(DISTINCT id) FROM tableName) totalCount,
id,name,number
FROM tableName
or by using CROSS JOIN
SELECT x.totalCount,
a.id, a.name, a.number
FROM tableName a, (SELECT COUNT(DISTINCT id) totalCount
FROM tableName) x
You should try :
SELECT id,name,number, (SELECT COUNT(DISTINCT name) FROM YourTableName) FROM YourTableName
Good luck
SELECT COUNT(DISTINCT id) would be faster than using column name.
SELECT (SELECT COUNT(DISTINCT id) FROM tableName) as 'count',
id,name,number
FROM tableName
SELECT COUNT(id) AS count , id, name, number
FROM
(
SELECT id
FROM tableA
WHERE xyz
) as t1
JOIN tableB using (id)
GROUP BY id, name, number