How to optimize sql select statement? - mysql

I am having an issue with SQL select statement. I am trying to get the percentage using below logic.
For example, I have two tables. One is TableA and another TableB
TableA has column ID, A1, A2.., Get total distinct count of A1 as "X".
TableB has column ID, B1, B2, FK_A1. Get count of B2 as "Y".
Get (Y/X)*100 as Total Percentage.
I was able to do it using subqueries but would like to use a simple and effective statement. Is it possible to get all the above 3 cases in one select statement? Your help would be highly appreciated.
Select
(Select count(distinct A1) from TableA) As C1,
(Select count(B2) from TableB Inner Join TableA ON TableB.FK_A1=TableA.A1)
C2)

Try this query
SELECT ( COUNT(B2) / COUNT(DISTINCT A1) ) * 100 AS TOTAL_PERC FROM TABLEA A INNER JOIN TABLEB B ON TABLEB.FK_A1 = TABLEA.A1;

You can use a simple join between two tables:
SELECT
( COUNT(DISTINCT A1) / COUNT(B2) ) * 100 AS PRCNTG
FROM
TABLEA A
INNER JOIN TABLEB B ON TABLEB.FK_A1 = TABLEA.A1;
apply DISTINCT on B2 if needed in your case.
apply OUTER join if needed in your case.
Cheers!!

You can use inner join with group by to achieve this. You can also use cte to make your distinct records separately.
; with cte as (
select A1, Count(distinct A1) as CountA from tableA)
, ct as (
select distinct FK_A1 , count(b2) as Count from tableB)
select 100 * ct.count/cte.CountA as totalpercentage
from cte
inner join ct
on cte.A1=ct.FK_A1

Related

How to filter out duplicates from a union query?

I have two similar SELECT queries that retrieve data from the same table "my_table".
-- 1st select
SELECT
my_table.id,
a,
b
FROM my_table
JOIN table2 ON u = v
JOIN table3 ON x = y
UNION ALL
-- 2st select
SELECT
my_table.id,
a,
b
FROM my_table
JOIN table2 ON r = s
JOIN table3 ON t = u
Duplicates are to be filtered out under the following conditions:
If the second select returns an id that is already present in the 1st select, it should be discarded.
Is there an easy solution without using a common table expression?
Note: The SQL does not have to be a UNION and can also be changed.
UNION filters out duplicate rows by default. UNION ALL does not remove duplicates.
But the duplicates are based on all columns being identical, not just the id column. If a given id value occurs in both queries, but any of the other two columns are different, then it counts as a distinct row.
If you want to reduce the result to a single row per id, the use a GROUP BY:
SELECT id, ...aggregate expressions...
FROM (
SELECT my_table.id, a, b ...
UNION
SELECT my_table.id, a, b ...
) AS t
GROUP BY id;
When you GROUP BY id, then any other expressions of the outer select-list must be in aggregate functions like MAX() or SUM(), etc.
The reason it is important to use an aggregate function is that when there are multiple rows with the same id value which you want to reduce to one row, what value should be displayed for a and b?
Example:
id
a
b
4
12
24
4
18
28
If you group by id, you would get one row for id=4, but what value for the other two columns?
id
a
b
4
?
?
Read https://dev.mysql.com/doc/refman/8.0/en/group-by-handling.html for more details on this. Or my answer to Reason for Column is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause
You must use an aggregate function, which includes GROUP_CONCAT() to append all the values from that column in a comma-separated list. Or you can use ANY_VALUE() which picks one of the values from that column arbitrarily.
I think this should do it:
-- 1st select
SELECT
my_table.id,
a,
b
FROM my_table
JOIN table2 ON u = v
JOIN table3 ON x = y
WHERE id NOT IN (
SELECT
my_table.id,
FROM my_table
JOIN table2 ON r = s
JOIN table3 ON t = u
)
UNION ALL
-- 2st select
SELECT
my_table.id,
a,
b
FROM my_table
JOIN table2 ON r = s
JOIN table3 ON t = u

How to select rows which have the biggest value of a column?

I don't know if my title is understandable or not, may be someone can help edit my title?
All I want to do is, for example:
I have a table like this
Engineering appears 5 times with different article_category_abbr, and I want to select only one row with the biggest value of num.
Here, it will be Engineering-ENG-192, and Geriatrics&Gerontology will be Geriatrics&Gerontology-CLM-26
But I don't know how to do it on the whole table using mysql
Join your table to a subquery which finds the greatest num value for each sc group.
SELECT t1.*
FROM yourTable t1
INNER JOIN
(
SELECT sc, MAX(num) AS max_num
FROM yourTable
GROUP BY sc
) t2
ON t1.sc = t2.sc AND
t1.num = t2.max_num;
You can have a subquery that gets the largest value for each sc and the resulting rows will then be joined with the table itself based from two columns - sc and num.
SELECT a.*
FROM tableName a
INNER JOIN
(
SELECT sc, MAX(num) AS Num
FROM tableName
GROUP BY sc
) b ON a.sc = b.sc
AND a.num = b.num
Here's a Demo
USE MAX function and GROUP BY like this. Here is more information.
SELECT myID, classTitle, subField, MAX(score) FROM myTable GROUP BY myID, classTitle, subField

Mysql to return count as 0 instead of nothing

I have seen the other questions here at stack but i am not able to solve my particular problem following is the example table
status area
a1 b1
a2 b1
a1 b2
following is the query i use
select count(*) as count, area, status from table group by area,status
which gives me
1 b1 a1
1 b1 a2
1 b2 a1
i want
0 b2 a2
also to be shown
i understand i have to use a join to do this so i tried the following
select Distinct(t1.status) as ds,t2.* from table as t1
right join
(select count(*), area, status from table group by area,status) t2
on t2.status=t1.status;
but this just gives me the same result without the 0
also tried this but apparently there is a syntax error i cant figure out
select t1.count,t2.ds,t1.area from
(select count(*) as count, area, status as ws from table group by area,status) t1
left join select distinct(status) as ds from table as t2
on t1.ws=t2.ds
As Tim pointed out in his comment: you need to introduce the missing record somehow, mysql will not be able to figure it out on its own.
What you can do is to create a cartesian join between status and area values using a self join, then left join the original table again on this and do the group by and count:
select t1.status, t2.area, count(t3.status)
from (select distinct status from yourtable) t1
join (select distinct area from yourtable) t2
left join yourtable t3 on t3.status=t1.status and t3.area=t2.area
group by t1.status, t2.area
Try below query
SELECT COUNT(t.area) count, c_t.area, c_t.status
FROM
(SELECT * FROM (SELECT DISTINCT status
FROM table) t_status
CROSS JOIN (SELECT DISTINCT area
FROM table) t_area
) c_t
LEFT JOIN table t
ON t.area = c_t.area
AND t.status = c_t.status
GROUP BY c_t.area, c_t.status;
Hope this should help you out.
The COUNT() function returns the number of rows that matches a specified criteria. That means that the smallest result of it will be zero by definition - i.e. COUNT() cannot return "nothing".

How to merge two tables on ID

I have two tables, who have the exact same columns. I want to merge table b into table a and if the dataset has the same ID, I want to use the dataset of table b.
I tried something like:
SELECT *
FROM
((SELECT
*
FROM
tableA) UNION (SELECT
*
FROM
tableB)) AS temp
GROUP BY temp.ID
ORDER BY temp.ID
but that gave me a mix of both tables.
You can do this using union all along with some additional logic:
select b.*
from b
union all
select a.*
from a
where not exists (select 1 from b where b.id = a.id);

SQL GROUP_CONCAT alias in WHERE

Here's my sql statement:
SELECT
tA.a1, GROUP_CONCAT(tB.b2) AS b2
FROM
tableA tA
LEFT JOIN
tableB tB ON tA.a2 = tB.b1
WHERE
CONCAT(tA.a1, b2) LIKE '%somestring%'
GROUP BY tA.a1;
I get an sql error saying something along the lines of "unknown column name b2 in WHERE".
SELECT
tA.a1, GROUP_CONCAT(tB.b2) AS b2
FROM
tableA tA
LEFT JOIN
tableB tB ON tA.a2 = tB.b1
GROUP BY tA.a1
HAVING
CONCAT(tA.a1, b2) LIKE '%somestring%';
You can't use aliases in WHERE clause - but in your case that's even senseless, because WHERE applies filter to rows that will be grouped while GROUP_CONCAT() collects rows that are already grouped
You may do that, for example, with subquery:
SELECT *
FROM
(SELECT
tA.a1 AS ta1, GROUP_CONCAT(tB.b2) AS b2
FROM
tableA tA
LEFT JOIN
tableB tB ON tA.a2 = tB.b1
GROUP BY tA.a1) AS grouped
WHERE
CONCAT(ta1, grouped.b2) LIKE '%somestring%'
for filtering aggregate functions, use HAVING instead of WHERE
select a, group_concat(b) as b_aggregate from
tbl
where concat(a,b) like "%somestring%" -- not aggregate
group by a
having concat(a, group_concat(b)) like "%somestring%" -- aggregate