Two select statements - mysql

SELECT COUNT( DISTINCT `student_id` )
FROM a
WHERE `marks` >=90
The above gives me count of student with marks greater than 90,
COUNT( DISTINCT `student_id` )
4
Now I want two answers, one with marks > 90 and another >80
Something like this
COUNT( DISTINCT `student_id` ) |COUNT( DISTINCT `student_id` )
4 5
This is what I have tried after google search
select
count_1 =( SELECT COUNT( DISTINCT `student_id` )
FROM a
WHERE `marks` >=90),
count_2 =( SELECT COUNT( DISTINCT `student_id` )
FROM a
WHERE `marks` >=80)

I think the simpliest answer would be by using implicit boolean condition since MySQL supports it,
SELECT SUM(marks >= 90) Count_1,
SUM(marks >= 80) Count_1
FROM a
WHERE marks >= 80
The WHERE clause makes the searching more faster since it will filter the records first than running through all records.

You can use a CASE expression with an aggregate function:
SELECT
sum(case when `marks` >= 90 then 1 else 0 end) count_1,
sum(case when `marks` >= 80 then 1 else 0 end) count_2
FROM a
Then if you have more counts to get you will add more case expressions.
Or you can use:
SELECT
count(distinct case when `marks` >= 90 then `student_id` end) count_1,
count(distinct case when `marks` >= 80 then `student_id` end) count_2
FROM a

Related

Using Exists when Union All is present in the Sub Query

I wanted to modify the query I wrote using the IN operator to using the Exists operator. However, the problem is that I have union all in my subquery. As I do not have much experience with using exists in such scenario I was looking for some help.
This was the initial query:
select APP_NUM,SBM_INDV_ID
from T1004_APP_INDV indv
where INDV.APP_NUM = 'T23952717'
and INDV.SBM_INDV_ID in
(select EMPL.INDV_ID
from DC_EMPLOYMENT EMPL
where EMPL.LOST_EMPLOYMENT_SW = 'N'
and EMPL.TERMINATION_DT is null
and (EMPL.EFF_END_DT is null
or EMPL.EFF_END_DT >= TRUNC(SYSDATE))
union all
select UNEARN.INDV_ID
from DC_UNEARNED_INCOME UNEARN
where UNEARN.EFF_END_DT is null
or UNEARN.EFF_END_DT >= TRUNC(SYSDATE)
union all
select RBINC.INDV_ID
from DC_ROOM_BOARD_INCOME RBINC
where RBINC.EFF_END_DT is null
or RBINC.EFF_END_DT >= TRUNC(SYSDATE)
union all
select SELFEMP.INDV_ID
from DC_SELF_EMP_INCOME SELFEMP
where SELFEMP.EFF_END_DT is null
or SELFEMP.EFF_END_DT >= TRUNC(SYSDATE)
);
I tried changing the initial query as follows:
select APP_NUM,SBM_INDV_ID
from T1004_APP_INDV APPINDV
where APPINDV.APP_NUM = 'T23952717'
and exists
(select EMPL.INDV_ID
from DC_EMPLOYMENT EMPL
where APPINDV.SBM_INDV_ID = EMPL.INDV_ID
and EMPL.LOST_EMPLOYMENT_SW = 'N'
and EMPL.TERMINATION_DT is null
and (EMPL.EFF_END_DT is null
or EMPL.EFF_END_DT >= TRUNC(SYSDATE))
union all
select UNEARN.INDV_ID
from DC_UNEARNED_INCOME UNEARN
where APPINDV.SBM_INDV_ID = UNEARN.INDV_ID
and UNEARN.EFF_END_DT is null
or UNEARN.EFF_END_DT >= TRUNC(SYSDATE)
union all
select RBINC.INDV_ID
from DC_ROOM_BOARD_INCOME RBINC
where APPINDV.SBM_INDV_ID = RBINC.INDV_ID
and RBINC.EFF_END_DT is null
or RBINC.EFF_END_DT >= TRUNC(SYSDATE)
union all
select SELFEMP.INDV_ID
from DC_SELF_EMP_INCOME SELFEMP
where APPINDV.SBM_INDV_ID = SELFEMP.INDV_ID
and SELFEMP.EFF_END_DT is null
or SELFEMP.EFF_END_DT >= TRUNC(SYSDATE));
Is the second query correct? Does it produce the same result as the previous query?
Thanks
I think splitting this into separate queries makes it simpler for the optimizer:
where APPINDV.APP_NUM = 'T23952717' and
( exists (select 1
from DC_EMPLOYMENT EMPL
where APPINDV.SBM_INDV_ID = EMPL.INDV_ID and
EMPL.LOST_EMPLOYMENT_SW = 'N' and
EMPL.TERMINATION_DT is null and
(EMPL.EFF_END_DT is null or EMPL.EFF_END_DT >= TRUNC(SYSDATE)
) or
exists (select 1
from DC_UNEARNED_INCOME UNEARN
where APPINDV.SBM_INDV_ID = UNEARN.INDV_ID and
( UNEARN.EFF_END_DT is null and or UNEARN.EFF_END_DT >= TRUNC(SYSDATE) )
) or
. . .
)

How do I retrieve data from two tables?

I was on the "hospital_payment_data" table.
I want to call up the data of the number of data, the cache_account_received sum, and the total_medical_bills sum, and then bring up the mount sum value from the cash_recipit_rowtable to express. What should I do?
hospital_payment_data
cash_receipt_row
I want result
However, sending the following queries results in the following:
SELECT
COUNT(*) as total,
SUM(cash_amount_received) AS sum_cash_amount_received,
COUNT(
IF(total_medical_bills >= 100000 AND
cash_amount_received , total_medical_bills, NULL)
) as obligatory_issue,
SUM(
IF(total_medical_bills >= 100000 AND
cash_amount_received , cash_amount_received, NULL)
) as sum_obligatory_issue,
SUM(amount) AS sum_amount
FROM (
SELECT total_medical_bills, cash_amount_received, amount
FROM hospital_payment_data, cash_receipt_row
) AS a
wrong result
Try this.
SELECT
COUNT(*) as total,
SUM(cash_amount_received) AS sum_cash_amount_received,
COUNT(
IF(total_medical_bills >= 100000 AND
cash_amount_received , total_medical_bills, NULL)
) as obligatory_issue,
SUM(
IF(total_medical_bills >= 100000 AND
cash_amount_received , cash_amount_received, NULL)
) as sum_obligatory_issue,
SUM(amount) AS sum_amount
FROM (
SELECT total_medical_bills, cash_amount_received, amount
FROM hospital_payment_data, cash_receipt_row
WHERE hospital_payment_data.id = cash_receipt_row.id
) AS a
Never use commas in the FROM clause. Always use proper, explicit, standard, readable JOIN syntax.
You can also simplify your counting logic in MySQL. There is no need for IF() or a subquery:
SELECT COUNT(*) as total,
SUM(cash_amount_received) AS sum_cash_amount_received,
SUM( total_medical_bills >= 100000 AND
obligatory_issue <> 0
) as obligatory_issue,
SUM(CASE WHEN total_medical_bills >= 100000
THEN cash_amount_received
END) as sum_obligatory_issue,
SUM(amount) AS sum_amount
FROM hospital_payment_data hpd JOIN
cash_receipt_row crr
ON hpd.id = crr.id;
You'll notice that where conditional logic is needed, then this uses the standard SQL construct, CASE, rather than IF.

Mysql clear code in one line

i was searching on google and in stackoverflow.com how can i write this code clearly and nice but found nothing.
Any idea?
MySQL query:
SELECT COUNT(*) AS `allInvoice`,
(SELECT COUNT(*) FROM `ocr_entity` WHERE `status` = 0) AS `new`,
(SELECT COUNT(*) FROM `ocr_entity` WHERE `status` = 10) AS `notTemplate`,
(SELECT COUNT(*) FROM `ocr_entity` WHERE `status` = 50) AS `withMistake`,
(SELECT COUNT(*) FROM `ocr_entity` WHERE `status` = 100) AS `finished`,
(SELECT COUNT(*) FROM `ocr_entity` WHERE `status` = 200) AS `skipped`
FROM `ocr_entity`;
Use conditional aggregation:
SELECT COUNT(*) AS `allInvoice`,
SUM( `status` = 0 ) AS `new`,
SUM( `status` = 10 ) AS `notTemplate`,
SUM( `status` = 50 ) AS `withMistake`,
SUM( `status` = 100 ) AS `finished`,
SUM( `status` = 200 ) AS `skipped`
FROM `ocr_entity`;
MySQL treats booleans as numbers in a numeric context, with 0 for false and 1 for true (which is why SUM() works).

What would be the best indices for this kind of slow MySQL-Query? (InnoDB)

this kind of MySQL-Query is very slow at the moment.
What would be the best indices for this to speed it up? (InnoDB)
SELECT item_id,
Group_concat(storage_nr SEPARATOR ',') AS storage_nr,
Group_concat(condition SEPARATOR ',') AS condition,
Group_concat(number SEPARATOR ',') AS number,
Group_concat(price SEPARATOR ',') AS price,
last_calc
FROM items
WHERE number > 0
AND bottomlimit IS NOT NULL
AND condition IN (1, 2, 3)
AND ( price_date IS NULL
OR price_date < Date_sub(Now(), INTERVAL 1 hour) )
AND ( NOT ( price = bottomlimit
AND pricebefore = bottomlimit
AND pricebefore2 = bottomlimit )
OR price IS NULL
OR pricebefore IS NULL
OR pricebefore2 IS NULL
OR Date(price_date) <> Curdate() )
GROUP BY item_id
ORDER BY last_calc
LIMIT 20
Thanks a lot in advance!
Best regards!
I'm inclined to agree with Gordon's comment for the most part, but one thing you could try is conditional aggregation. (This could be one of those unique scenarios where processing more of the data and discarding what you don't need is faster than filtering to what you do need, especially since it seems some OR conditions tend to wreck index use in MySQL).
Something similar to this might help.
SELECT item_id
, Group_concat(
IF(condition IN (1, 2, 3)
AND ( price_date IS NULL OR price_date < Date_sub(Now(), INTERVAL 1 hour) )
AND ( NOT ( price = bottomlimit AND pricebefore = bottomlimit AND pricebefore2 = bottomlimit )
OR price IS NULL
OR pricebefore IS NULL
OR pricebefore2 IS NULL
OR Date(price_date) <> Curdate()
)
, storage_nr, NULL)
SEPARATOR ','
) AS storage_nr
, [etc...]
FROM items
WHERE number > 0 AND bottomlimit IS NOT NULL
GROUP BY item_id
HAVING storage_nr IS NOT NULL
ORDER BY last_calc
LIMIT 20
You only hope to use indexed is to break down your complex query in a UNION of more simple and indexed query.
First of all, you can use the FROM [subquery] syntax
SELECT item_id,
Group_concat(storage_nr SEPARATOR ',') AS storage_nr,
Group_concat(condition SEPARATOR ',') AS condition,
Group_concat(number SEPARATOR ',') AS number,
Group_concat(price SEPARATOR ',') AS price,
last_calc
FROM
SUBQUERY
AS items
GROUP BY item_id
ORDER BY last_calc
LIMIT 20
This could be your subquery:
SELECT *
FROM items
WHERE number > 0
AND bottomlimit IS NOT NULL
AND condition IN (1, 2, 3)
AND price_date IS NULL
AND ( NOT ( price = bottomlimit
AND pricebefore = bottomlimit
AND pricebefore2 = bottomlimit )
OR price IS NULL
OR pricebefore IS NULL
OR pricebefore2 IS NULL
OR Date(price_date) <> Curdate() )
UNION ALL
SELECT *
FROM items
WHERE number > 0
AND bottomlimit IS NOT NULL
AND condition IN (1, 2, 3)
AND price_date < Date_sub(Now(), INTERVAL 1 hour)
AND ( NOT ( price = bottomlimit
AND pricebefore = bottomlimit
AND pricebefore2 = bottomlimit )
OR price IS NULL
OR pricebefore IS NULL
OR pricebefore2 IS NULL
OR Date(price_date) <> Curdate() )
Define the following index
- condition
- price_date
- bottomLimit
Please give me a feedback on the results. Thanks.
Thanks for helping me!
The best solution for me was after testing everything to convert the table to MyISAM.
With this change i can speed the query up 3 to 5 times faster - from round about 12 seconds to less then 3 seconds.

MYSQL GROUP BY multiple derived tables?

I have this query which does some calculations based on some derived tables that are linked with an INNER JOIN.
At the moment I have a WHERE clause which pulls out one id at a time. But how can I make it list all the ids?
I have tried GROUP BY in various places but can't figure it out.
My query so far is as follows:
SELECT
equipment_id,
service_duration,
available_duration,
(available_duration / service_duration)*100 AS availability
FROM (
SELECT
SUM(service_end_time - service_start_time) AS service_duration
FROM(
SELECT equipment_id,
(CASE
END) AS service_start_time,
(CASE
END) AS service_end_time
FROM t1
WHERE equipment_id = 'EX123'
)AS A
) AS B
JOIN(
SELECT equipment_id,
SUM(available_end_time - available_start_time) AS available_duration
FROM (
SELECT equipment_id,
(CASE
END) AS available_start_time,
(CASE
END) AS available_end_time
FROM t2
WHERE equipment_id = 'EX123'
) AS C
) AS D
ON equipment_id=D.equipment_id
What I want to do is replace the WHERE clause with a GROUP BY to list all the ids, or similar, but getting that to work is beyond my skill level... Any help greatly appreciated :)
Try below:
SELECT
equipment_id, service_duration, available_duration,
(available_duration / service_duration)*100 AS availability
FROM
(
SELECT equipment_id,
SUM(service_end_time - service_start_time) AS service_duration
FROM
(
SELECT equipment_id,
(CASE ... END) AS service_start_time,
(CASE ... END) AS service_end_time
FROM t1
) AS A
GROUP BY equipment_id
) AS B
JOIN
(
SELECT equipment_id,
SUM(available_end_time - available_start_time) AS available_duration
FROM
(
SELECT equipment_id,
(CASE ... END) AS available_start_time,
(CASE ... END) AS available_end_time
FROM t2
) AS C
GROUP BY equipment_id
) AS D
ON equipment_id=D.equipment_id
Try this (replace my field names with your field names):
SELECT
a.emp_id,
service_duration,
available_duration
FROM
(
SELECT
emp_id,
SUM(service_end_time - service_start_time) AS service_duration
FROM
data
GROUP BY
emp_id
) a
JOIN
(
SELECT
emp_id,
SUM(available_end_time - available_start_time) AS available_duration
FROM
data
GROUP BY
emp_id
) b
ON a.emp_id = b.emp_id
GROUP BY
a.emp_id