I've got 2 questions regarding sqlite3 queries using 2 tables - sqlite

A) List each lecturer together with each module they teach and the number of students studying that module, in order of the lecturer name.
B) Output the number of modules in which everyone passed the module (assuming pass mark is 40).

For A, you need to join the 2 tables and then group by lecturer, module and count the number of rows for each group (each row corresponds to a student):
select t.lecturer, t.module, count(*) numberofstudents
from teaches t inner join studies s
on s.module = t.module
group by t.lecturer, t.module
order by t.lecturer
For B, use NOT EXISTS to find the modules where all grades are >= 40 and count them:
select count(distinct module) numberofmodules
from studies s
where not exists (
select 1 from studies
where module = s.module and grade < 40
)

Related

Count Persons in tree of Groups with a single query

I'm writing a query in SQLite, for Android, with a schema like this (extremely simplified here, just the fields I need)
GROUP
group_id primary_key,
parent_group_id nullable
PERSON
person_id primary_key,
parent_group
I need to count the number of persons in a group and in its descendant groups, given the group_id of the group I want to count for. I think I need a CTE query and I've been reading all morning about them, but I'm not grasping how they work.
You're on the right track with needing a CTE. Something like:
WITH tree AS
(SELECT g.group_id AS root,
g.group_id AS parent,
p.person_id AS person
FROM "group" AS g
LEFT JOIN person AS p ON g.group_id = p.parent_group
WHERE g.group_id = #desired_group
UNION ALL
SELECT t.root, g.group_id, p.person_id
FROM tree AS t
JOIN "group" AS g ON t.parent = g.parent_group_id
LEFT JOIN person AS p on g.group_id = p.parent_group)
SELECT count(DISTINCT person)
FROM tree;
Start by selecting the desired group and its members, and then recursively select all members of groups with the given parent group. Finally, count all the unique users that were found.
db<>fiddle example.
I powered through all the articles I could find and via a lot of trials and errors, I got here (please note that in my real database person is model):
WITH RECURSIVE is_in_group(group_id, group_name, parent_group_id) AS(
SELECT gr.group_id, gr.group_name, gr.parent_group_id FROM _group as gr WHERE gr.group_id = :groupId
UNION ALL
SELECT g.group_id, g.group_name, g.parent_group_id FROM _group as g
JOIN is_in_group as c ON g.parent_group_id = c.group_id
)
SELECT q.group_id, q.group_name, count(m.model_id) as model_count FROM is_in_group as q
LEFT JOIN _model m ON m.parent_group_id = group_id
GROUP BY q.group_id
This will give me a list of groups (including the root one), with a group_id, group_name and a model_count of models in each group. With this I can simply sum to get the total or look at the row with the searched group_id to know how many models are just in this group.

SQLite order results by smallest difference

In many ways this question follows on from my previous one. I have a table that is pretty much identical
CREATE TABLE IF NOT EXISTS test
(
id INTEGER PRIMARY KEY,
a INTEGER NOT NULL,
b INTEGER NOT NULL,
c INTEGER NOT NULL,
d INTEGER NOT NULL,
weather INTEGER NOT NULL);
in which I would typically have entries such as
INSERT INTO test (a,b,c,d,weather) VALUES(1,2,3,4,30100306);
INSERT INTO test (a,b,c,d,weather) VALUES(1,2,3,4,30140306);
INSERT INTO test (a,b,c,d) VALUES(1,2,5,5,10100306);
INSERT INTO test (a,b,c,d) VALUES(1,5,5,5,11100306);
INSERT INTO test (a,b,c,d) VALUES(5,5,5,5,21101306);
Typically this table would have multiple rows with the some/all of b, c and d values being identical but with different a and weather values. As per the answer to my other question I can certainly issue
WITH cte AS (SELECT *, DENSE_RANK() OVER (ORDER BY (b=2) + (c=3) + (d=4) DESC) rn FROM test where a = 1) SELECT * FROM cte WHERE rn < 3;
No issues thus far. However, I have one further requirement which arises as a result of the weather column. Although this value is an integer it is in fact a composite where each digit represents a "banded" weather condition. Take for example weather = 20100306. Here 2 represents the wind direction divided up into 45 degree bands on the compass, 0 represents a wind speed range, 1 indicates precipitation as snow etc. What I need to do now while obtaining my ordered results is to allow for weather differences. Take for example the first two rows
INSERT INTO test (a,b,c,d,weather) VALUES(1,2,3,4,30100306);
INSERT INTO test (a,b,c,d,weather) VALUES(1,2,3,4,30140306);
Though otherwise similar they represent rather different weather conditions - the fourth number is four as opposed to 0 indicating a higher precipitation intensity brand. The WITH cte... above would rank the first two rows at the top which is fine. But what if I would rather have the row that differs the least from an incoming "weather condition" of 30130306? I would clearly like to have the second row appearing at the top. Once again, I can live with the "raw" result returned by WITH cte... and then drill down to the right row based on my current "weather condition" in Java. However, once again I find myself thinking that there is perhaps a rather neat way of doing this in SQL that is outwith my skill set. I'd be most obliged to anyone who might be able to tell me how/whether this can be done using just SQL.
You can sort the results 1st by DENSE_RANK() and 2nd by the absolute difference of weather and the incoming "weather condition":
WITH cte AS (
SELECT *,
DENSE_RANK() OVER (ORDER BY (b=2) + (c=3) + (d=4) DESC) rn
FROM test
WHERE a = 1
)
SELECT a,b,c,d,weather
FROM cte
WHERE rn < 3
ORDER BY rn, ABS(weather - ?);
Replace ? with the value of that incoming "weather condition".

sqlite: how to get a count of group counts

I have a SQLite table of user actions on a website. Each row is the same action on a web site, just different time/date, tagged with a user id. The table has more than 20Million entries. I understand how to get a count by user (i.e. A took the action 3 times, B 4, C 2, D 4, etc.) using the group by function by user id. In other words this works fine:
select count(uid) as event_count
from table
group by uid
What I want is the data for a statistical distribution which is a count of the number of users who only took 1 action, a count of users that took 2 actions, etc. Said another way: The list might look something like:
1 | 339,440
2 | 452,555
3 | 99,239
5 | 20,209
etc. ...
I could use the having event_count = n clause and just rerun the query for every integer until all were accounted for but that seems silly. There must be a way that I can get a single list with two columns: the group size and the count of the users who all took the exact same number of actions.
As simply as adding another grouping above:
select event_count, count(*) as users_count
from
(select count(uid) as event_count
from table
group by uid) t
group by event_count
order by event_count

COUNT and DISTINCT without group by

I've two tables. One with clients, other with products bought by clients.
When I want to get the list of all products and clients, I do:
SELECT client_name, prod_id FROM TAB_CLIENT
INNER JOIN TAB_PROD ON prod_client = client_name
So I get (eg)
Henri - Potatoes
Henri - Chocolate
Tom - Beer
Nice. Now I want to know how many different clients I have.
So I tried to use COUNT and DISTINCT. Like this:
SELECT COUNT(DISTINCT client_name) AS num_client, client_name, prod_id
FROM TAB_CLIENT
INNER JOIN TAB_PROD ON prod_client = client_name
I want to get:
2 - Henri - Potatoes
2 - Henri - Chocolate
2 - Tom - Beer
So "2" in first colum as the whole number of different clients is 2 (Henri and Tom), name of the client at second colum and name of product as third colume.
But in fact if I add the count(distinct), I get only ONE result (seems like if there was a "group by" on num_client). And I don't want to GROUP BY on client_name as in this case I'll loose the product.
Is it possible to perform that in one query, or do I have to perform one select for counting and an other to get the result?
I notice all examples given in Stack are about Count/Distinct returning only number but not the number AND results.
Thanks
PS: I'm running MariaDB.
SELECT
( SELECT COUNT(DISTINCT client_name) FROM TAB_CLIENT ) AS Number_of_clients,
client_name,
prod_id
FROM TAB_CLIENT
INNER JOIN TAB_PROD ON prod_client = client_name

Cognos: Count the number of occurences of a distinct id

I'm making a report in Cognos Report Studio and I'm having abit of trouble getting a count taht I need. What I need to do is count the number of IDs for a department. But I need to split the count between initiated and completed. If an ID occures more than once, it is to be counted as completed. The others, of course, will be initiated. So I'm trying to count the number of ID occurences for a distinct ID. Here is the query I've made in SQl Developer:
SELECT
COUNT((CASE WHEN COUNT(S.RFP_ID) > 8 THEN MAX(CT.GCT_STATUS_HISTORY_CLOSE_DT) END)) AS "Sales Admin Completed"
,COUNT((CASE WHEN COUNT(S.RFP_ID) = 8 THEN MIN(CT.GCT_STATUS_HISTORY_OPEN_DT) END)) as "Sales Admin Initiated"
FROM
ADM.B_RFP_WC_COVERAGE_DIM S
JOIN ADM.B_GROUP_CHANGE_REQUEST_DIM CR
ON S. RFP_ID = CR.GCR_RFP_ID
JOIN ADM.GROUP_CHANGE_TASK_FACT CT
ON CR.GROUP_CHANGE_REQUEST_KEY = CT.GROUP_CHANGE_REQUEST_KEY
JOIN ADM.B_DEPARTMENT_DIM D
ON D.DEPARTMENT_KEY = CT.DEPARTMENT_RESP_KEY
WHERE CR.GCR_CHANGE_TYPE_ID = '20'
AND S.RFP_LOB_IND = 'WC'
AND S.RFP_AUDIT_IND = 'N'
AND CR.GCR_RECEIVED_DT BETWEEN '01-JAN-13' AND '31-DEC-13'
AND D.DEPARTMENT_DESC = 'Sales'
AND CT.GCT_STATUS_IND = 'C'
GROUP BY S.RFP_ID ;
Now this works. But I'm not sure how to translate taht into Cognos. I tried doing a CASE taht looked liek this(this code is using basic names such as dept instead of D.DEPARTMENT_DESC):
CASE WHEN dept = 'Sales' AND count(ID for {DISTINCT ID}) > 1 THEN count(distinct ID)END)
I'm using count(distinct ID) instead of count(maximum(close_date)). But the results would be the same anyway. The "AND" is where I think its being lost. It obviously isn't the proper way to count occurences. But I'm hoping I'm close. Is there a way to do this with a CASE? Or at all?
--EDIT--
To make my question more clear, here is an example:
Say I have this data in my table
ID
---
1
2
3
4
2
5
5
6
2
My desired count output would be:
Initiated Completed
--------- ---------
4 2
This is because two of the distinct IDs (2 and 5) occure more than once. So they are counted as Completed. The ones that occure only once are counted as Initiated. I am able to do this in SQl Dev, but I can't figure out how to do this in Cognos Report Studio. I hope this helps to better explaine my issue.
Oh, I didn't quite got it originally, amending the answer.
But it's still easiest to do with 2 queries in Report Studio. Key moment is that you can use a query as a source for another query, guaranteeing proper group by's and calculations.
So if you have ID list in the table in Report Studio you create:
Query 1 with dataitems:
ID,
count(*) or count (1) as count_occurences
status (initiated or completed) with a formula: if (count_occurences > 1) then ('completed') else ('initiated').
After that you create a query 2 using query one as source with just 2 data items:
[Query1].[Status]
Count with formula: count([Query1].[ID])
That will give you the result you're after.
Here's a link to doco on how to nest queries:
http://pic.dhe.ibm.com/infocenter/cx/v10r1m0/topic/com.ibm.swg.ba.cognos.ug_cr_rptstd.10.1.0.doc/c_cr_rptstd_wrkdat_working_with_queries_rel.html?path=3_3_10_6#cr_rptstd_wrkdat_working_with_queries_rel

Resources