SQL JOIN to count and SUM issue - mysql

I know that I am missing something, I have tried several different ways but I am overlooking (or trying too hard). Can someone please tell me where I am wrong on this SQL?
SELECT id,
COUNT(id) AS dupBlocks
FROM tbl_duplicates8 INNER JOIN (
tbl_accounts8,
tbl_delaccounts,
tbl_bad_bots,
tbl_log,
tbl_ipban,
tbl_ipban8
) ON (
tbl_accounts8.SUM(num_attacks) AND
tbl_delaccounts.SUM(noattacks) AND
tbl_bad_bots.COUNT(id) AND
tbl_log.COUNT(id) AND
tbl_ipban.COUNT(txt_ip) AND
tbl_ipban8.COUNT(ip)
);
I did notice this MySQL Join two tables count and sum from second table but it gives me a null on return.
Any help would be appreciated.
To further the question for a better answer, this is what I am currently doing:
$statsresults['newIPBan'] = $db->query("SELECT COUNT(ip) AS newIPBan FROM tbl_ipban8;");
$statsresults['oldIPBan'] = $db->query("SELECT COUNT(txt_ip) AS oldIPBan FROM tbl_ipban;");
$statsresults['log_blocks'] = $db->query("SELECT COUNT(id) AS logBlocks FROM tbl_log;");
$statsresults['badbots'] = $db->query("SELECT COUNT(id) AS badBots FROM tbl_bad_bots;");
$statsresults['del_num_attacks'] = $db->query("SELECT SUM(noattacks) AS deltotalattacks FROM tbl_delaccounts;");
$statsresults['num_attacks'] = $db->query("SELECT SUM(num_attacks) AS totalattacks FROM tbl_accounts8;");
$statsresults['dup_blocks'] = $db->query("SELECT COUNT(id) AS dupBlocks FROM tbl_duplicates8;");
Which will return this:
| ['newIPBan0newIPBan'] = String(6) "289033"
| ['oldIPBan0oldIPBan'] = String(6) "125723"
| ['log_blocks0logBlocks'] = String(4) "6481"
| ['badbots0badBots'] = String(5) "15310"
| ['del_num_attacks0deltotalattacks'] = String(9) "119494860"
| ['num_attacks0totalattacks'] = String(8) "25286478"
| ['dup_blocks0dupBlocks'] = String(6) "179916"
So right now it is calling the database 7 times to get each sum or count. I was hoping to change that to 1 database call and returning the sum of them all.

This is one way that you can combine them:
select (newIPBan + oldIPBan + logBlocks + badBots + deltotalattacks + totalattacks + dupBlocks
) as NumIPs
from (SELECT COUNT(ip) AS oldIPBan FROM tbl_ipban8) ipb8 cross join
(SELECT COUNT(txt_ip) AS newIPBan FROM tbl_ipban) ipb cross join
(SELECT COUNT(id) AS logBlocks FROM tbl_log) l cross join
(SELECT COUNT(id) AS badBots FROM tbl_bad_bots) bb cross join;
(SELECT coalesce(SUM(noattacks), 0) AS deltotalattacks FROM tbl_delaccounts) da cross join
(SELECT coalesce(SUM(num_attacks), 0) AS totalattacks FROM tbl_accounts8) ta cross join
(SELECT COUNT(id) AS dupBlocks FROM tbl_duplicates8) d;

Edit
I would personally create a view for the various metrics, along the following lines:
CREATE VIEW vwMetrics AS
SELECT 'newIPBan' AS Metric, COUNT(ip) AS Value FROM tbl_ipban8
UNION
SELECT 'oldIPBan', COUNT(txt_ip) FROM tbl_ipban
UNION
SELECT 'logBlocks', COUNT(id) FROM tbl_log
UNION
SELECT 'badBots', COUNT(id) AS badBots FROM tbl_bad_bots
UNION
SELECT 'deltotalattacks', SUM(noattacks) FROM tbl_delaccounts
UNION
SELECT 'totalattacks', SUM(num_attacks) FROM tbl_accounts8
UNION
SELECT 'dupBlocks', COUNT(id) FROM tbl_duplicates8;
And then you can aggregate the components:
SELECT SUM(Value) AS TotalOfEverything
FROM vwMetrics;
The benefit of the view is that you can drill down into the components for detail / debugging purposes, rather than arrive at a magical total - there might be some reuse for the view to be had elsewhere in your system.
Prototype Fiddle here
(+Thanks for clarifying the question)

Related

How to SUM the result of two different SQL query?

I want to SUM these two different queries. I joined both with USING function. but it shows the two results in two different cells.
I need a single result to SUM these two queries where the answer should be 4069.
The CODE
SELECT COUNT(*) Active_Projects FROM
(SELECT ProjectID, ProjectStatusID, ClientID
FROM project) a
INNER JOIN
(SELECT ProjectStatusID, ClientID, ProjectStatusName
FROM ProjectStatus) b
ON (a.ProjectStatusID = b.ProjectStatusID AND a.ClientID = b.ClientID)
WHERE ProjectStatusName LIKE '%active%'
AND a.ClientID = 4
UNION
SELECT COUNT(*) Total_Projects
FROM Project
WHERE ClientID=4
Picture
Screenshot
Screenshot Here
Try this. select query1+ query2;
select (
SELECT COUNT(*) Active_Projects FROM
(SELECT ProjectID, ProjectStatusID, ClientID
FROM project) a
INNER JOIN
(SELECT ProjectStatusID, ClientID, ProjectStatusName
FROM ProjectStatus) b
ON (a.ProjectStatusID = b.ProjectStatusID AND a.ClientID = b.ClientID)
WHERE ProjectStatusName LIKE '%active%'
AND a.ClientID = 4
) + (
SELECT COUNT(*) Total_Projects
FROM Project
WHERE ClientID=4) ;

SQL - Divide One Query by Another

I am trying to create a query which returns the workout percentage completion...
Workout Percentage Completion =
((Sum of LogEntries WHERE date = IN list of dates and WorkoutID =1 ) /
(Sum of all set numbers WHERE WorkoutID = 1))
x 100
Below is what I currently have, at the moment it is only returning the result of the first query.
What should I change for the query to run correctly?
SELECT
(
(
SELECT COUNT(LogEntriesID)
FROM LogEntriesTable
LEFT JOIN ExerciseWorkoutJunctionTable
ON ExerciseWorkoutJunctionTable.ExerciseWorkoutJunctionID =
LogEntriesTable.JunctionID
WHERE LogEntriesTable.date IN (
"14-05-2020", "15-05-2020", "16-05-2020", "17-05-2020",
"18-05-2020", "19-05-2020", "20-05-2020"
)
AND ExerciseWorkoutJunctionTable.WorkoutID = 1
) / (
SELECT sum(SetNumber)
FROM ExerciseWorkoutGoalsTable
LEFT JOIN ExerciseWorkoutJunctionTable
ON ExerciseWorkoutJunctionTable.ExerciseWorkoutJunctionID =
ExerciseWorkoutGoalsTable.JunctionID
WHERE ExerciseWorkoutJunctionTable.WorkoutID = 1
)
)
Your first SELECT statement is doing an OUTER JOIN but then you have a WHERE clause that is selecting non-NULL values from the ExerciseWorkoutJunctionTable table, so I suspect you might as well be doing an INNER JOIN.
When you have two queries, try:
SET #sum = (SELECT SUM(SetNumber) etc ....);
SELECT (COUNT(LogEntriesID) * 100 / #sum) AS percentage
FROM etc.
If you are using MySQL >= 8.0 you should be able to use window functions like this which breakdown your query into more readable sections.
with entries as (
SELECT COUNT(LogEntriesID) as log_entry_count
FROM LogEntriesTable as l
LEFT JOIN ExerciseWorkoutJunctionTable as e ON
e.ExerciseWorkoutJunctionID = l.JunctionID
WHERE l.date IN ("14-05-2020","15-05-2020","16-05-2020","17-05-2020","18-05-2020","19-05-2020","20-05-2020")
AND e.WorkoutID = 1
),
sets as (
SELECT sum(SetNumber) as set_sum
FROM ExerciseWorkoutGoalsTable as eg
LEFT JOIN ExerciseWorkoutJunctionTable ej
ON ej.ExerciseWorkoutJunctionID = eg.JunctionID
WHERE ej.WorkoutID = 1
)
select ((select log_entry_count from entries) / (select set_sum from sets)) * 100 as workout_completion_pct

mysql get count of columns having a value above and below a certain value in a row

As I get a row, I also want to grab the number of rows that are below a value in the selected row and a separate count of rows that are above a value of the selected row.
What's the best method of doing this?
The following obviously is wrong, but might illustrate what I mean...
SELECT page.*,COUNT(less_than_threshold.id) as `smaller`,
COUNT(greater_than_threshold.id) as `bigger`
FROM `learning_pages` as pages
JOIN learning_pages as less_than_threshhold on page.class_id=less_than_threshold.class_id
AND less_than_threshhold.content_count < page.content_count
JOIN learning_pages as greater_than_threshold ON
page.class_id=greater_than_threshold.class_id AND
greater_than_threshold.content_count>page.content_count
WHERE page.id=5
EDIT
As requested:
learning_pages
id (BIG INT) | content_count (INT) | type (VARCHAR) | author_id (BIG INT) | date_updated (DATETIME)| class_id(BIG_INT)
Truncated values
(id,content_count)
VALUES(1,3), (2,4), (3,3), (4,5), (5,3), (6,2),(7,1), (8,3), (9,2)
...
With
SELECT page.*,COUNT(less_than_threshold.id) as `smaller`
FROM `learning_pages` as pages
JOIN learning_pages as less_than_threshhold on page.class_id=less_than_threshold.class_id
AND less_than_threshhold.content_count < page.content_count
WHERE page.id=5
I get a smaller count of 3, which is correct. Adding the bigger count to that is the issue. The solution I have is a sub_query which I fear is not the best solution here...
SELECT page.*, count(i.id) as `bigger` FROM
(SELECT lp.*,COUNT(clp.id) as `smaller` FROM `learning_pages` as lp
JOIN learning_pages as clp on lp.class_id=clp.class_id AND
clp.content_count<lp.content_count WHERE lp.id=5) as page
JOIN learning_pages as i ON page.class_id =i.class_id WHERE page.id=5
You can DISTINCT the result. Use the following query:
SELECT page.*,
COUNT( DISTINCT less_than_threshold.id) as `smaller`,
COUNT( DISTINCT greater_than_threshold.id) as `bigger`
FROM `learning_pages` as page
JOIN learning_pages as less_than_threshold
on page.class_id=less_than_threshold.class_id
AND less_than_threshold.content_count < page.content_count
JOIN learning_pages as greater_than_threshold
ON page.class_id=greater_than_threshold.class_id
AND greater_than_threshold.content_count>page.content_count
WHERE page.id=5
You can try something like this using queries in select statement:
SELECT pages.*,
(
SELECT COUNT(*)
FROM pages
WHERE pages.id < 5
) AS less_than_threshold,
(
SELECT COUNT(*)
FROM pages
WHERE pages.id > 5
) AS greater_than_threshold
FROM pages
WHERE pages.id = 5;

SQL Query behavior

I'm bogged in trying to figure out why query a is returning different records than query b. Both queries have seemingly same purpose yet a is returning 500 and b 3500.
this is query a:
SELECT DISTINCT ODE.OrderBillToID
FROM APTIFY.dbo.vwVwOrderDetailsKGExtended ODE
WHERE ProductID IN (2022, 1393)
AND LTRIM(RTRIM(ODE.OrderStatus)) <> 'Cancelled'
AND LTRIM(RTRIM(ODE.OrderType)) <> 'Cancellation'
AND LTRIM(RTRIM(ODE.cancellationStatus)) <> 'FULLY CANCELLED'
UNION
SELECT DISTINCT ID
FROM APTIFY.dbo.vwPersons WHERE City = 'A'
UNION
SELECT DISTINCT RecordID
FROM APTIFY.dbo.vwTopicCodeLinks WHERE TopicCodeID = 16 AND Value = 'Yes, Please'
query b:
SELECT
APTIFY..vwPersons.ID
FROM
APTIFY..vwPersons
WHERE
( APTIFY..vwPersons.ID IN (
SELECT
vwMeetingRegistrants.ID
FROM
APTIFY.dbo.vwMeetings vwMeetings
INNER JOIN APTIFY.dbo.vwMeetingRegistrants vwMeetingRegistrants
ON vwMeetings.ID=vwMeetingRegistrants.ActualMeetingID WHERE
vwMeetings.ProductID = 2022
)
OR
APTIFY..vwPersons.ID IN (
SELECT
vwMeetingRegistrants.ID
FROM
APTIFY.dbo.vwMeetings vwMeetings
INNER JOIN APTIFY.dbo.vwMeetingRegistrants vwMeetingRegistrants
ON vwMeetings.ID=vwMeetingRegistrants.ActualMeetingID WHERE
vwMeetings.ProductID = 1393
)
OR
APTIFY..vwPersons.City = N'Albany' )
OR
((
APTIFY..vwPersons.ID IN (
SELECT
RecordID
FROM
APTIFY.dbo.vwTopicCodeLinks vwTopicCodeLinks
WHERE
vwTopicCodeLinks.TopicCodeID = 16
)
AND
APTIFY..vwPersons.ID IN (
SELECT
RecordID
FROM
APTIFY.dbo.vwTopicCodeLinks vwTopicCodeLinks
WHERE
vwTopicCodeLinks.Value = N'Yes, Please'
) )
)
vwMeetingsRegistrants from the b query are producing the same records as orderkgdetailsextended from query. I cannot see ANY difference in those queries - which perhaps shows my lack of understanding the query behaviour.
BIG Thanks for any points guys! :)
As it came out, incorrectly structured query is a result of badly configured application, Aptify.

SQL Left Join Multiple Tables and count values conditionally in each table

I am having some trouble putting together a SQL statement properly because I don't have much experience SQL, especially aggregate functions. Safe to say I don't really know what I'm doing outside of the basic SQL structure. I can do regular joins, but not complex ones.
I have some tables: 'Survey', 'Questions', 'Session', 'ParentSurvey', and 'ParentSurveyQuestion'. Structurally, a survey can have questions, it can have users that started the survey (a session), and it can have a parent survey whose questions get imported into the current survey.
What I want to do is get information for a each survey in the Survey table; total questions it has, how many sessions have been started (conditionally, ones that have not finished), and the number of questions in the parents survey. The three joined tables can but do not have to contain any values, and if they don't then 0 should be returned by COUNT. The common field in three of the tables is a variation of 'survey_id'
Here is my SQL so far, I put the table structure below it.
SELECT
`kp_survey_id`,
COALESCE( q.cnt, 0 ) AS questionsAmount,
COALESCE( s.cnt, 0 ) AS sessionsAmount
COALESCE( p.cnt, 0 ) AS parentQAmount,
FROM `Survey`
LEFT JOIN <-- I'd like the count of questions for this survey
( SELECT COUNT(*) AS cnt
FROM Questions
GROUP BY kf_survey_id ) q
ON Survey.kp_survey_id = Questions.kf_survey_id
LEFT JOIN
( SELECT COUNT(*) AS cnt <-- I'd like the count of started sessions for this survey
FROM Session
WHERE session_status = 'started' <-- should this be Session.session_status?
GROUP BY kf_survey_id ) s
ON Survey.kp_survey_id = Session.kf_survey_id
LEFT JOIN
( SELECT COUNT(*) AS cnt <-- I'd like the count of questions in the parent survey with this survey id
FROM ParentSurvey
GROUP BY kp_parent_survey_id ) p
ON Survey.kf_parent_survey_id = ParentSurveyQuestion.kf_parent_survey_id
'kp' prefix means primary key, while 'kf' prefix means foreign key
Structure:
Survey: 'kp_survey_id' | 'kf_parent_survey_id'
Question: 'kp_question_id' | 'kf_survey_id'
Session: 'kp_session_id' | 'kf_survey_id' | 'session_status'
ParentSurvey: 'kp_parent_survey_id' | 'survey_name'
ParentSurveyQuestion: 'kp_parent_question_id' | 'kf_parent_survey_id'
There are also other columns in each table like 'name' or 'account_id', but i don't think they matter in this case
I'd like to know if I'm doing this correctly or if I'm missing something. I'm repurposing some code I found here on stackoverflow and modifying it to meet my needs, as I haven't seen conditional aggregation for more than three tables on this site.
My expected output is something like:
kp_survey_id | questionsAmount | sessionsAmount | parentQAmount
1 | 3 | 0 | 3
2 | 0 | 5 | 3
I think you were pretty close -- just need to fix your joins and include the survey id in the subqueries to use in those joins:
SELECT
`kp_survey_id`,
COALESCE( q.cnt, 0 ) AS questionsAmount,
COALESCE( s.cnt, 0 ) AS sessionsAmount
COALESCE( p.cnt, 0 ) AS parentQAmount,
FROM `Survey`
LEFT JOIN
( SELECT COUNT(*) cnt, kf_survey_id AS cnt
FROM Questions
GROUP BY kf_survey_id ) q
ON Survey.kp_survey_id = q.kf_survey_id
LEFT JOIN
( SELECT COUNT(*) cnt, kf_survey_id
FROM Session
WHERE session_status = 'started'
GROUP BY kf_survey_id ) s
ON Survey.kp_survey_id = s.kf_survey_id
LEFT JOIN
( SELECT COUNT(*) cnt, kp_parent_survey_id
FROM ParentSurvey
GROUP BY kp_parent_survey_id ) p
ON Survey.kf_parent_survey_id = p.kp_parent_survey_id
One thing you need to do is correct your joins. When you are joining to a subquery, you need to use the alias of the subquery. In your case you are using the alias of the table being used in the subquery.
Another thing you need to change is to include the field you wish to use in your JOIN in the subquery.
Make these changes and try running. Do you get an error or the desired results?
SELECT
`kp_survey_id`,
COALESCE( q.cnt, 0 ) AS questionsAmount,
COALESCE( s.cnt, 0 ) AS sessionsAmount
COALESCE( p.cnt, 0 ) AS parentQAmount,
FROM `Survey`
LEFT JOIN <-- I'd like the count of questions for this survey
( SELECT kf_survey_id, COUNT(*) AS cnt
FROM Questions
GROUP BY kf_survey_id ) q
ON Survey.kp_survey_id = q.kf_survey_id
LEFT JOIN
( SELECT kf_survey_id, COUNT(*) AS cnt <-- I'd like the count of started sessions for this survey
FROM Session
WHERE session_status = 'started' <-- should this be Session.session_status?
GROUP BY kf_survey_id ) s
ON Survey.kp_survey_id = s.kf_survey_id
LEFT JOIN
( SELECT kp_parent_survey_id, COUNT(*) AS cnt <-- I'd like the count of questions in the parent survey with this survey id
FROM ParentSurvey
GROUP BY kp_parent_survey_id ) p
ON Survey.kf_parent_survey_id = p.kf_parent_survey_id