Problems counting joined rows with conditional - sql

I am having some problems with MySQL and selecting column count from a joined table. I have a feeling this is going to require a sub-select statement to fetch the rows I would like to join instead of doing it in my primary where condiutional.
I am trying to select a list of project which are linked to a certain user. I would also like to include a count of tasks which are assigned to this project and this user (but only those of a certain status).
I have it working almost - which means not at all. It will only return project id 1, and not id 2. This is because of my 'tasks.status<9' where clause.
Any help would be great. Let me know if I need to explain anything else.
+-------------------------+
| projects_to_users |
+-----------+-------------+
+ user_id | project_id |
+-----------+-------------+
+ 1 | 1 |
+-----------+-------------+
+ 1 | 2 |
+-----------+-------------+
+-------------------------+
| projects |
+--------------+----------+
+ project_id | name |
+--------------+----------+
+ 1 | Foo |
+--------------+----------+
+ 2 | Bar |
+--------------+----------+
+------------------------------------------------+
| tasks |
+-----------+--------------+----------+----------+
+ task_id | project_id | status | name |
+-----------+--------------+----------+----------+
+ 1 | 1 | 1 | Do it |
+-----------+--------------+----------+----------+
+ 2 | 1 | 1 | Do itt |
+-----------+--------------+----------+----------+
+ 3 | 1 | 9 | Do not |
+-----------+--------------+----------+----------+
SELECT count( tasks.task_id ) AS task_count, projects.*
FROM (projects)
LEFT JOIN tasks ON tasks.project_id = projects.project_id
LEFT JOIN projects_to_users ON projects.project_id=projects_to_users.project_id
WHERE tasks.status<9
AND tasks.assigned_user_id = '1'
AND projects_to_users.user_id = '1'
GROUP BY projects.project_id
RETURNS:
+--------------+--------------+--------+
+ task_count | project_id | name |
+--------------+--------------+--------+
+ 2 | 1 | Foo |
+--------------+--------------+--------+
SHOULD RETURN:
+--------------+--------------+--------+
+ task_count | project_id | name |
+--------------+--------------+--------+
+ 2 | 1 | Foo |
+--------------+--------------+--------+
+ 0 | 2 | Bar |
+--------------+--------------+--------+

If you want to show per-project, per-user task counts, you should also group by the projects_to_users.user_id field.
Also, the reason why you're not seeing the zero-count project is because your WHERE condition expects there to have been a match by means of the LEFT JOIN clauses. If you move the WHERE criteria into the joins themselves, this should work better for you.
For example:
SELECT count( tasks.task_id ) AS task_count,
projects.*
FROM (projects)
LEFT JOIN tasks ON tasks.project_id = projects.project_id AND tasks.status < 9 AND tasks.assigned_user_id = '1'
LEFT JOIN projects_to_users ON projects.project_id=projects_to_users.project_id AND projects_to_users.user_id = '1'
GROUP BY projects.project_id

Why can't you do a simple join like this:
select count(*) from projects_to_users inner join tasks on projects_to_users.project_id=tasks.project_id;

Related

SQL Server view with [with, distinct, STRING_AGG]

I would like to add an distinct concatenated string for each row to this view.
SELECT
i.Id, i.Name, oavName.AttributeValue AS BelongingToName,
ie.ExecutionDate, i.Categories
FROM
dbo.[GAB.Inspection] AS i
INNER JOIN
dbo.Object AS o ON o.Id = i.ObjectId
INNER JOIN
dbo.ObjectHistory AS oh ON oh.ObjectId = o.Id
AND oh.IsLast = 'true'
LEFT OUTER JOIN
dbo.[GAB.InspectionExecution] AS ie ON i.Id = ie.InspectionId
AND ie.IsLast = 'true'
INNER JOIN
dbo.ObjectAttributeValue AS oavName ON oavName.ObjectHistoryId = oh.Id
INNER JOIN
dbo.ObjectAttributeTemplate AS oatName ON oavName.ObjectAttributeTemplateId = oatName.Id
AND oatName.AttributeLabel = 'Name'
This returns the following result:
+----+-----------------+------------------+---------------+------------+
| Id | Name | BelongingToName | ExecutionDate | Categories |
+----+-----------------+------------------+---------------+------------+
| 1 | Demo Inspection | Demo Main Object | 2022-04-14 | DemoCat |
+----+-----------------+------------------+---------------+------------+
Now I have an other table where I get all the available intervals in it like this:
SELECT InspectionId, Turnus AS Interval
FROM [GAB.QuestionTemplate]
Result:
+--------------+----------+
| InspectionId | Interval |
+--------------+----------+
| 1 | 1 |
| 1 | 1 |
| 1 | 1 |
| 1 | 5 |
| 1 | 5 |
+--------------+----------+
And distincted
SELECT InspectionId, Turnus AS Interval
FROM [GAB.QuestionTemplate]
GROUP BY InspectionId, Turnus AS Interval
+--------------+----------+
| InspectionId | Interval |
+--------------+----------+
| 1 | 1 |
| 1 | 5 |
+--------------+----------+
So far so good... Now what I need is an concatenated String based on the Intervals.
WITH Turnusse AS
(
SELECT Turnus
FROM [GAB.QuestionTemplate]
GROUP BY Turnus
)
SELECT
STRING_AGG(ISNULL(Turnus, ' '), ';') WITHIN GROUP (ORDER BY Turnus ASC) AS Intervals
FROM
Turnusse
Result:
+-----------+
| Intervals |
+-----------+
| 1;5 |
+-----------+
What I now need is to add this Intervals belonging to the InspectionId as an separate column in the view.
Should look like this then:
+----+-----------------+------------------+---------------+------------+-----------+
| Id | Name | BelongingToName | ExecutionDate | Categories | Intervals |
+----+-----------------+------------------+---------------+------------+-----------+
| 1 | Demo Inspection | Demo Main Object | 2022-04-14 | DemoCat | 1;5 |
+----+-----------------+------------------+---------------+------------+-----------+
This is what I don't get... How can I implement the STRING_AGG into the view to give the expected output?
The table [GAB.QuestionTemplate] has the InspectionId which is the i.Id in the view.

how to bake in a record count in a sql query

I have a query that looks like this:
select id, extension, count(distinct(id)) from publicids group by id,extension;
This is what the results looks like:
id | extension | count
-------------+-------------------------+-------
18459154909 | 12333 | 1
18459154909 | 9891114 | 1
18459154919 | 43244 | 1
18459154919 | 8776232 | 1
18766145025 | 12311 | 1
18766145025 | 1122111 | 1
18766145201 | 12422 | 1
18766145201 | 14141 | 1
But what I really want is for the results to look like this:
id | extension | count
-------------+-------------------------+-------
18459154909 | 12333 | 2
18459154909 | 9891114 | 2
18459154919 | 43244 | 2
18459154919 | 8776232 | 2
18766145025 | 12311 | 2
18766145025 | 1122111 | 2
18766145201 | 12422 | 2
18766145201 | 14141 | 2
I'm trying to get the count field to show the total number of records that have the same id.
Any suggestions would be appreciated
I think you want to count distincts extentions, not ids.
Run this query:
select id
, extension
(select count(*) from publicids p1 where p.id = p1.id ) distinct_id_count
from publicids p
group by id,extension;
This is more or less the same as Pastor's answer. Depending on what the optimizer does it might be faster with higher record count source tables.
select p.id, p.extension, p2.id_count
from publicids p
inner join (
select id, count(*) as id_count
from publicids group by id
) as p2 on p.id = p2.id

SQL SELECT JOIN FILTER

I try to explain the problem as good as possible.
I have multiple tables:
project, group, period.
The connection table of these three is called project_status.
I will quickly show there content
Project
| projectID | name | date |
| ------------------------|
| 1 | test | 2015 |
| 2 | test | 2015 |
Group
| groupID| name |
| --------------|
| 1 | ab |
| 2 | cd |
Period
| periodID | status |
| ---------------------|
| 1 | 0 | #inactive
| 2 | 1 | #active
| 3 | 2 | #new
Project stats
| projectID | groepID | periodID |
| -------------------------------|
| 1 | 1 | 2 | #active period
| 1 | 1 | 3 | #new period
Now in a gui you can select a period. Is the period active then i dont show the
project because it's in use (active). Now when i select a period with status new there must be a check to determine:
Is this project already in a new period
The problem is when i write a query there is always the active period. How could i write a query that only checks in project status for status new
i have tried the following query
SELECT projectID, name
FROM project
WHERE projectID IN
(
SELECT ps.projectID
FROM project_status as ps
JOIN period as per
ON ps.periodID = per.periodID
WHERE per.status = 0
AND per.stats != 2
)
OR projectID NOT IN
(
SELECT projectID
FROM project_status
)
Your query looks right.
Only remove
AND per.periode_status != 2
What is periode_status ? You didnt explain
WHERE per.status = 0
AND per.periode_status != 2 -- remove it
You could use a cross apply, something like this would select projectID & name where periodID = 3:
SELECT projectID, name FROM project a
CROSS APPLY (SELECT projectID,periodID FROM project_status WHERE projectID = a.projectID) b
WHERE b.periodID = 3

PostgreSQL select all from one table and join count from table relation

I have two tables, post_categories and posts. I'm trying to select * from post_categories;, but also return a temporary column with the count for each time a post category is used on a post.
Posts
| id | name | post_category_id |
| 1 | test | 1 |
| 2 | nest | 1 |
| 3 | vest | 2 |
| 4 | zest | 3 |
Post Categories
| id | name |
| 1 | cat_1 |
| 2 | cat_2 |
| 3 | cat_3 |
Basically, I'm trying to do this without subqueries and with joins instead. Something like this, but in real psql.
select * from post_categories some-type-of-join posts, count(*)
Resulting in this, ideally.
| id | name | count |
| 1 | cat_1 | 2 |
| 2 | cat_2 | 1 |
| 3 | cat_3 | 1 |
Your help is greatly appreciated :D
You can use a derived table that contains the counts per post_category_id and left join it to the post_categories table
select p.*, coalesce(t1.p_count,0)
from post_categories p
left join (
select post_category_id, count(*) p_count
from posts
group by post_category_id
) t1 on t1.post_category_id = p.id
select post_categories.id, post_categories.name , count(posts.id)
from post_categories
inner join posts
on post_category_id = post_categories.id
group by post_categories.id, post_categories.name

SQlite join same table twice with different "on" statement

I couldn't find answer for my question, and I don't know if my query is correct and this could be a SQLite issue, please help me solve the problem.
I have two tables in my database:
processTable {id}
taskTable {id, processId, amount, done}
There is a many-to-one relation (one process can have multiple tasks assigned). The "amount" and "done" are integer values that provides task progress information. If "done" >= "amount", the task is done. I need to query database to get something like that:
+---------+-----------+------------+
| process | tasksDone | tasksCount |
+---------+-----------+------------+
| 1 | 1 | 3 |
+---------+-----------+------------+
| 2 | 2 | 5 |
+---------+-----------+------------+
Basing on data that I have in my tables:
processTable
+----+
| id |
+----+
| 1 |
+----+
| 2 |
+----+
tasksTable
+----+-----------+--------+------+
| id | processId | amount | done |
+----+-----------+--------+------+
| 1 | 1 | 10 | 10 | <- this task is done
+----+-----------+--------+------+
| 2 | 1 | 15 | 5 |
+----+-----------+--------+------+
| 3 | 1 | 80 | 5 |
+----+-----------+--------+------+
| 4 | 2 | 25 | 0 |
+----+-----------+--------+------+
| 5 | 2 | 60 | 60 | <- this task is done
+----+-----------+--------+------+
| 6 | 2 | 30 | 15 |
+----+-----------+--------+------+
| 7 | 2 | 40 | 40 | <- this task is done
+----+-----------+--------+------+
| 8 | 2 | 100 | 50 |
+----+-----------+--------+------+
So, I wrote this query:
SELECT processTable.id AS process,
COUNT(tasksTableDone.id) AS tasksDone,
COUNT(tasksTableAll.id) AS tasksCount
FROM processTable
LEFT JOIN tasksTable AS tasksTableAll
ON tasksTableAll.processId = processTable.id
LEFT JOIN tasksTable AS tasksTableDone
ON tasksTableDone.processId = processTable.id
AND
tasksTableDone.done >= tasksTableDone.amount
But what I've got is:
+---------+-----------+------------+
| process | tasksDone | tasksCount |
+---------+-----------+------------+
| 1 | 3 | 3 |
+---------+-----------+------------+
| 2 | 5 | 5 |
+---------+-----------+------------+
I was trying run the query with only one join at a time, and everything was working well.
Query with first join only:
SELECT processTable.id AS process,
COUNT(tasksTableAll.id) AS tasksCount
FROM processTable
LEFT JOIN tasksTable AS tasksTableAll
ON tasksTableAll.processId = processTable.id
Result:
+---------+------------+
| process | tasksCount |
+---------+------------+
| 1 | 3 |
+---------+------------+
| 2 | 5 |
+---------+------------+
Query with second join only:
SELECT processTable.id AS process,
COUNT(tasksTableDone.id) AS tasksDone
FROM processTable
LEFT JOIN tasksTable AS tasksTableDone
ON tasksTableDone.processId = processTable.id
AND
tasksTableDone.done >= tasksTableDone.amount
Result:
+---------+-----------+
| process | tasksDone |
+---------+-----------+
| 1 | 1 |
+---------+-----------+
| 2 | 2 |
+---------+-----------+
How to use this two joins within one query to get proper results? I know that instead of JOIN I could use another SELECT, but I think it would be more expensive in the performance meaning.
You can implement a CASE statement with an aggregate:
Version using SUM()
SELECT p.id AS process,
sum(case when t.amount = t.done then 1 else 0 end) AS tasksDone,
count(p.id) AS tasksCount
FROM processTable p
LEFT JOIN tasksTable t
ON t.processId = p.id
group by p.id
See SQL Fiddle with Demo
Version using COUNT():
SELECT p.id AS process,
count(case when t.amount = t.done then 1 else null end) AS tasksDone,
count(p.id) AS tasksCount
FROM processTable p
LEFT JOIN tasksTable t
ON t.processId = p.id
group by p.id
See SQL Fiddle with Demo
Edit, after your comment you can wrap this in a select to get the progress:
select process,
tasksDone,
tasksCount,
(tasksDone / tasksCount) progress
from
(
SELECT p.id AS process,
count(case when t.amount = t.done then 1 else null end) AS tasksDone,
count(p.id) AS tasksCount
FROM processTable p
LEFT JOIN tasksTable t
ON t.processId = p.id
group by p.id
) src