How to do a complicated SQLite Join with Count - sqlite

With SQLite, Given the tables below
_id Name
1 StudentA
2 StudentB
3 StudentC
and
id StudentId Test Score
1 1 A 5
2 1 B 5
3 1 A 6
4 1 B 6
5 2 A 3
6 2 B 3
7 2 A 4
or in SQL Form
BEGIN TRANSACTION;
DROP TABLE IF EXISTS "Results";
CREATE TABLE IF NOT EXISTS "Results" (
"id" INTEGER PRIMARY KEY AUTOINCREMENT,
"StudentId" INTEGER,
"Test" TEXT,
"Score" INTEGER,
FOREIGN KEY("StudentId") REFERENCES "Students"("_id")
);
DROP TABLE IF EXISTS "Students";
CREATE TABLE IF NOT EXISTS "Students" (
"_id" INTEGER PRIMARY KEY AUTOINCREMENT,
"Name" TEXT NOT NULL
);
INSERT INTO "Results" ("id","StudentId","Test","Score") VALUES (1,1,'A',5);
INSERT INTO "Results" ("id","StudentId","Test","Score") VALUES (2,1,'B',5);
INSERT INTO "Results" ("id","StudentId","Test","Score") VALUES (3,1,'A',6);
INSERT INTO "Results" ("id","StudentId","Test","Score") VALUES (4,1,'B',6);
INSERT INTO "Results" ("id","StudentId","Test","Score") VALUES (5,2,'A',3);
INSERT INTO "Results" ("id","StudentId","Test","Score") VALUES (6,2,'B',3);
INSERT INTO "Results" ("id","StudentId","Test","Score") VALUES (7,2,'A',4);
INSERT INTO "Students" ("_id","Name") VALUES (1,'StudentA');
INSERT INTO "Students" ("_id","Name") VALUES (2,'StudentB');
INSERT INTO "Students" ("_id","Name") VALUES (3,'StudentC');
COMMIT;
I would like to show which students have missed which tests and by how many times
The SQL query below gets me close but not exactly what I need
SELECT s.Name, r.Test, COUNT(r.Test) AS Count
FROM Students s
LEFT OUTER JOIN Results r ON s._id = r.StudentId
GROUP BY s._id, r.Test
Gives me the results table:-
Name Test Count
StudentA A 2
StudentA B 2
StudentB A 2
StudentB B 1
StudentC NULL 0
BUT I would like the table as shown below:-
Name Test Count
StudentA A 2
StudentA B 2
StudentB A 2
StudentB B 1
StudentC A 0
StudentC B 0
Is there any way to do this with SQLite?

Cross join the table Students with the distinct Tests and then left join to the table Results:
WITH cte AS (
SELECT *
FROM Students
CROSS JOIN (SELECT DISTINCT Test FROM Results)
)
SELECT c.Name, c.Test, COUNT(r.Test) AS Count
FROM cte c
LEFT OUTER JOIN Results r ON c._id = r.StudentId AND c.Test = r.Test
GROUP BY c.Name, c.Test
See the demo.
Or without the CTE:
SELECT s.Name, t.Test, COUNT(r.Test) AS Count
FROM Students s
CROSS JOIN (SELECT DISTINCT Test FROM Results) t
LEFT OUTER JOIN Results r ON s._id = r.StudentId AND t.Test = r.Test
GROUP BY s.Name, t.Test
See the demo.
Results:
| Name | Test | Count |
| -------- | ---- | ----- |
| StudentA | A | 2 |
| StudentA | B | 2 |
| StudentB | A | 2 |
| StudentB | B | 1 |
| StudentC | A | 0 |
| StudentC | B | 0 |

Related

SQLite, Multiply table select with table identification

Is it possible to print table name in result, and how if it is...
in example:
SELECT * FROM TABLE1, TABLE2;
I'll get this result:
1 | 2 | 3
4 | 5 | 6
where the first row is from table1 and the second row from table2, but I want those table-names to be included in the output, like this:
1 | 2 | 3 | TABLE1
4 | 5 | 6 | TABLE2
Ah, I just need to put the table-name as a string literal in the SELECT:
SELECT
*,
'TABLE1' AS "name"
FROM
TABLE1
UNION
SELECT
*,
'TABLE2' AS "name"
FROM
TABLE2;

SQL Query: How to count occurrence of a specific value in a column without grouping on that column?

I have a table like this:
id | value
1 | a
1 | a
1 | b
1 | c
2 | a
2 | a
2 | a
2 | c
And I want to count(*) by id and then count(value==a) by id, which means this is the desired results:
id | total_counts | a_counts
1 | 4 | 2
2 | 4 | 3
I know how to do it by joining two subqueries, but is there an easier/faster way to do it? Like this pseudo-code:
SELECT id, COUNT(*) AS total_counts, COUNT(value==a) AS a_counts
FROM table
GROUP BY id
Not sure if there is a way to do the COUNT(value==a) part. Please kindly help.
You could use SUM:
SELECT id, COUNT(*) AS total_counts, SUM(value='a') AS a_counts
FROM table
GROUP BY id;
Or if you have SQLite 3.25 you could use windowed version:
SELECT /*DISTINCT*/
id,
COUNT(*) OVER(PARTITION BY id) AS total_counts,
COUNT(*) FILTER (WHERE value = 'a') OVER(PARTITION BY id) AS a_counts
FROM tab

update table in sorted order

I am maintaining table structure like below.
sortid | id | name
1 | 1 | aa
3 | 2 | cc
4 | 3 | cc
2 | 4 | bb
5 | 5 | dd
Where sortid is maintained according to ascending order of name.
Now I want to update name 'dd' to 'aa', such way that sort id is also updated to its correct value.
Update table set name="bb" where name like "dd";
After updating my table should become like below.
sortid | id | name
1 | 1 | aa
4 | 2 | cc
5 | 3 | cc
3 | 4 | bb
2 | 5 | aa
That sortid is the number of rows that would be sorted before this row.
So you can compute it by counting rows:
UPDATE MyTable
SET sortid = (SELECT COUNT(*)
FROM MyTable AS T2
WHERE T2.name < MyTable.name) +
(SELECT COUNT(*)
FROM MyTable AS T2
WHERE T2.name = MyTable.name
AND T2.id <= MyTable.id);
(The second subquery resolves duplicate sortid values that would result from duplicate names.)

How to get self-differences in sqlite table

does anyone know if there is a way to get this result in SQLite.
Given table with single column x like this:
x |
--
1
4
5
2
I need to add column dx, which is simply a difference x_i - x_{i-1} (except for the first one) like this:
x | dx |
-- --
1 | 0
4 | 3
5 | 1
2 | -3
Thanks a lot!
Update: given there is id column:
id | x |
-- --
1 | 1
2 | 4
3 | 5
4 | 2
Is it possible to obtain:
id | x | dx |
-- -- --
1 | 1 | 0
2 | 4 | 3
3 | 5 | 1
4 | 2 | -3
SQL tables have no implicit order associated with them. You must supply an ORDER BY clause to impose an order on the results.
What column would you order by to define the predecessor row for the subtraction? (Hint: there is none.)
With the addition of an id column per the revised question
sqlite> select id, x, (select t1.x - t2.x from t as t2 where id = t1.id - 1) from t as t1;
1|1|
2|4|3
3|5|1
4|2|-3
Or
sqlite> select id, x, coalesce((select t1.x - t2.x from t as t2 where id = t1.id - 1),0) from t as t1;
1|1|0
2|4|3
3|5|1
4|2|-3

Sql Query to group the data from two tables

I have two tables Department and Employee.
Department table looks like this:
ID DeptName
1 IT
2 CSE
3 ECE
Employee table :
ID DeptID EmployeeName Salary
1 1 John 10000
2 1 Bob 15000
3 2 Akon 12000
4 2 Smith 20000
Now I want to group the data in such a way that I get the following results which include these columns :
ID DeptName Employee
1 IT John,10000
Bob,15000
2 CSE Akon,12000
Smith,20000
Can we do something like this using SQL group functions or any other way?
Please help me.
Thanks,
Rajbir
This:
select final.deptId, d.deptName,
e3.employeename + ',' + cast(e3.salary as varchar) employee
from employee e3
left join (
select e1.id, e1.deptId from employee e1
left join employee e2
on e1.deptId = e2.deptId and e1.id > e2.id
where e2.id is null
) final on e3.id = final.id
left join department d on d.id = final.deptId
Results in:
+--------+----------+-------------+
| DEPTID | DEPTNAME | EMPLOYEE |
+--------+----------+-------------+
| 1 | IT | John,10000 |
| | | Bob,15000 |
| 2 | CSE | Akon,12000 |
| | | Smith,20000 |
+--------+----------+-------------+
Note that the "blank" values are actually filled with null values.
Let me know if you have any issue with it.

Resources