MySQL correlated sub-query in SELECT clause - mysql

I'm trying to execute a correlated sub-query in the SELECT clause of a MySQL query. How do I use the value of a row in another column inside the WHERE clause of my subquery?
I've been referencing the "Correlated Sub-Query Example" section of this webpage, but for some reason my query is not working the same.
Here is my code:
SELECT Year,
( SELECT COUNT(*)
FROM systems
WHERE SYSTEMTYPE = 'handheld' AND Year = sys.Year
) as handheld,
( SELECT COUNT(*)
FROM systems
WHERE SYSTEMTYPE = 'console' AND Year = sys.Year
) as console,
FROM systems as sys
WHERE Year IS NOT NULL
Basically, I'm trying to create a table that shows how many systems of each type were created for each year. When I run that query in MySQL Workbench it runs until the database connection expires. I can't see how this query is much different than the one on the website I am referencing.
Any help would be greatly appreciated! If it seems there is a better way I could go about this, I am open to those ideas as well. Thank you!

This may be issue of performance of a query. Such subquery needs to be executed for each row, thus it may take long to run. The query can be simplified by using group by:
select year, count(*) from systems
where Year is not null and systemtype = 'handled'
group by Year
UPDATE regarding comment:
what if I want to add more columns for different types of systems other than just the one type?
Use query:
select year,
sum(case when systemtype = 'handled' then 1 else 0 end) handled,
sum(case when systemtype = 'console' then 1 else 0 end) console
from systems
where Year is not null
group by Year

Could be you have a scope issue try use a inner join instead of a subquery for each rows
SELECT sys.Year, t.my_count
FROM systems as sys
INNER JOIN
( SELECT Year, COUNT(*) my_count
FROM systems
WHERE SYSTEMTYPE = 'handheld' AND Year = sys.Year
GROUP BY year
) t on t.year = sys.year
WHERE sys.Year IS NOT NULL

Use Group By statement instead of subqueries. Subqueries will make your query run much slower as more rows are added. Try this query to get number of systems made per type and per year:
SELECT
Year,
SYSTEMTYPE,
COUNT(*) as Total_systems_per_type_per_year
FROM systems
WHERE Year IS NOT NULL
GROUP BY Year, SYSTEMTYPE

Related

Rewrite MySQL code to retrieve average time

I don't know if that's a question for SO, if not please delete it. I am using the query below to calculate an average time it takes for a ticket on our service desk to get closed.
I don't have write permissions on the database, so I can't create functions, variables etc.
I strongly believe that there must be a better/nicer, more robust way to calculate that, than my query below, any thoughts?
What I want to avoid, if possible, is to recalculate the count value, which especially with all the where clauses makes the query a bit slow.
SELECT Count(hd_ticket.id) AS 'Tickets #',
ROUND(( Timestampdiff(hour, hd_ticket.created, hd_ticket.time_closed) /
(SELECT Count(hd_ticket.id)
FROM
hd_ticket
LEFT JOIN hd_status
ON hd_status_id = hd_status.id
WHERE
Month(
hd_ticket.time_closed) = 12
AND
Year
(hd_ticket.time_closed) = 2017
AND
hd_status.state LIKE '%close%'
AND
hd_ticket.hd_queue_id IN ( 8 )) )) AS
'AVG Closure Time'
FROM hd_ticket
LEFT JOIN hd_status
ON hd_status_id = hd_status.id
WHERE Month(hd_ticket.time_closed) = 12
AND Year(hd_ticket.time_closed) = 2017
AND hd_status.state LIKE '%close%'
AND hd_ticket.hd_queue_id IN ( 8 )
In a nutshell what the above query does is
SELECT COUNT(TICKETS) as 'Tickets #',
ROUND(TOTAL_TIME_TAKES_TO_CLOSE_TICKETS/COUNT(TICKETS + FILTERS)) as 'AVG Closure Time'
FROM HD_TICKET
SOME FILTERS
I would recommend:
SELECT Count(*) as Num_Tickets_Closed,
AVG( ( Timestampdiff(hour, t.created, t.time_closed) ) as AVG_CLosure_Time
FROM hd_ticket t LEFT JOIN
hd_status s
ON t.hd_status_id = s.id
WHERE t.time_closed >= '2017-12-01' AND
t.time_closed < '2018-01-01' AND
s.state LIKE '%close%' AND
t.hd_queue_id IN ( 8 ) ;
Notes:
First, you can just use AVG(). That greatly simplifies the query.
The date comparisons are made without functions. Although this likely has little impact in your case, it allows the use of indexes.
The names of the columns no longer have special characters, so they don't need to be escaped.
Table aliases make the query easier to write and to read.

Display zero in group by sql for a particular period

I am trying to run the following query to obtain the sales for each type of job for a particular period. However for certain months where there are no jobs of a particular job type performed no 0 is displayed in sales.
How can i display the zeros in such a condition.
Here is the sql query-
select Year(postedOn), month(postedOn), jobType, sum(price)
from tbl_jobs
group by jobType, year(postedOn), month(postedOn)
order by jobType, year(postedOn), month(postedOn)
Typically, this is where your all-purpose calendar or numbers table comes in to anchor the query with a consistent sequential set:
SELECT job_summary.*
FROM Calendar
CROSS JOIN (
-- you may not have though about this part of the problem, though
-- what about years/months with missing job types?
SELECT distinct jobType FROM tbl_jobs
) AS job_types
LEFT JOIN (
select Year(postedOn) AS year,month(postedOn) as month,jobType ,sum(price)
from tbl_jobs
group by jobType, year(postedOn), month(postedOn)
) job_summary
ON job_summary.jobType = job_types.jobType
AND job_summary.year = Calendar.year
AND job_summary.month = Calendar.month
WHERE Calendar.day = 1 -- Assuming your calendar is every day
AND calendar.date BETWEEN some_range_goes_here -- you don't want all time, right?
order by job_types.jobType, Calendar.year, Calendar.month

SQL query needs optimization

SELECT LM.user_id,LM.users_lineup_id, min( LM.total_score ) AS total_score
FROM vi_lineup_master LM JOIN
vi_contest AS C
ON C.contest_unique_id = LM.contest_unique_id join
(SELECT min( total_score ) as total_score
FROM vi_lineup_master
GROUP BY group_unique_id
) as preq
ON LM.total_score = preq.total_score
WHERE LM.contest_unique_id = 'iledhSBDO' AND
C.league_contest_type = 1
GROUP BY group_unique_id
Above query is to find the loser per group of game, query return accurate result but its not responding with large data. How can I optimize this?
You can try to move your JOINs to subqueries. Also, you should pay attention on your "wrong" GROUP BY usage on the outer query. In Mysql you can group by some columns and select others not specified in the group clause without any aggregation function, but the database can't ensure what data it will return to you. For the sake of consistency of your application, wrap them in an aggregation function.
Check if this one helps:
SELECT
MIN(LM.user_id) AS user_id,
MIN(LM.users_lineup_id) AS users_lineup_id,
MIN(LM.total_score) AS total_score
FROM vi_lineup_master LM
WHERE 1=1
-- check if this "contest_unique_id" is equals
-- to 'iledhSBDO' for a "league_contest_type" valued 1
AND LM.contest_unique_id IN
(
SELECT C.contest_unique_id
FROM vi_contest AS C
WHERE 1=1
AND C.contest_unique_id = 'iledhSBDO'
AND C.league_contest_type = 1
)
-- check if this "total_score" is one of the
-- "min(total_score)" from each "group_unique_id"
AND LM.total_score IN
(
SELECT MIN(total_score)
FROM vi_lineup_master
GROUP BY group_unique_id
)
GROUP BY LM.group_unique_id
;
Also, some pieces of this query may seem redundant, but it's because I did not want to change the filters you wrote, just moved them.
Also, your query logic seems a bit strange to me, based on the tables/columns names and how you wrote it... please, check the comments in my query which reflects what I understood of your implementation.
Hope it helps.

Best way to subtract SUM and COUNT with 2 table select?

I have came up with solution to count total of fields based on specific group, but it looks quite lengthy to get to the result i expect.
I have some basic knowledge when it comes to sql.
Is there obvious improvements to be made and why?
Why i would like to shorten this: Easier to implement in ORM type systems.
Changing scheme is not an option.
Schema and sample data: http://sqlfiddle.com/#!9/62df6
Query i'm using:
SELECT s.release_id,
(s.shipments_total - IFNULL(sh.shipment_entries, 0)) AS shipments_left
FROM
( SELECT release_id,
SUM(shipments) AS shipments_total
FROM subscriptions
WHERE is_paid = 1
AND shipments > 1
GROUP BY release_id ) AS s
LEFT JOIN
( SELECT release_id,
COUNT(*) AS shipment_entries
FROM shipments
GROUP BY release_id ) AS sh ON s.release_id = sh.release_id
Expected result on sample data is in sqlfiddle.
If you bring the condition in-line and remove the group by, then you don't need ifnull():
SELECT s.release_id,
(SUM(s.Shipments) -
(SELECT COUNT(*)
FROM shipments sh
WHERE sh.release_id = s.release_id
)
) AS shipments_left
FROM subscriptions s
WHERE is_paid = 1 AND shipments > 1
GROUP BY s.release_id;
The subquery returns 0 if nothing matches, not NULL (with the GROUP BY, it would return NULL). I am not sure if this is easier with your ORM model. Your original version is fine from a SQL point of view.
You can bring the join inline instead:
SELECT s.release_id,
SUM(s.Shipments) - IFNULL(( SELECT COUNT(*) AS shipment_entries
FROM shipments sh
WHERE sh.release_id = s.release_id
GROUP BY sh.release_id ), 0) AS shipments_left
FROM subscriptions s
WHERE is_paid = 1
AND shipments > 1
GROUP BY s.release_id
The execution plan for this is more performant too.

Query Is Not Summing As I Need

I am trying to write a CTE Query and I am way before a "New" title for CTE Queries. But I feel I am fairly close to getting the end game that I am after. My query works perfect until I throw in the CTE and even after including the CTE it still works perfect just gives each individual instance as opposed to the SUM like I need. What should I alter in my syntax so that the query only produces the SUM as I need?
;With CTE
As
(
SELECT
BadgeNum
,NameOnFile
,SUM((CONVERT(decimal(18,6),pyrll.hoursworked))) AS [Hours]
FROM
masterpayroll pyrll
Group By
BadgeNum,NameOnFile
)
SELECT
,SUM(pyrll.[Hours]) As [Hours Worked This Week]
,pyrll.NameOnFile As [Employee Name]
,COUNT(case when pf.arrest_status in ('Final', 'Complete',) And pf.supervisorSignoff IS NOT NULL THEN pf.ID else null end)
,COUNT(case when pf.arrest_status in ('Pending', 'Incomplete', 'On Hold') THEN pf.ID else null end)
FROM personelFiles pf
INNER JOIN CTE pyrll
ON pf.ID = pyrll.BadgeNum
WHERE pf.officerName Like 'Gat%'
GROUP BY pyrll.[Employee Name], pyrll.[Hours Worked This Week]
EDIT ---Top Data Set is what is returned from query - bottom data set is what I want to see returned.
EDIT # 2 - If their is a better way to write the query to still produce the desired result of the 2nd data set in my image below I am up for that as well!
You have aggregate column alias name in the group by, you need only the employee name
Change your group by clause from
GROUP BY pyrll.[Employee Name], pyrll.[Hours Worked This Week]
To
GROUP BY pyrll.NameOnFile