update table in sorted order - sqlite

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.)

Related

Sqlite / populate new column that ranks the existing rows

I've a SQLite database table with the following columns:
| day | place | visitors |
-------------------------------------
| 2021-05-01 | AAA | 20 |
| 2021-05-01 | BBB | 10 |
| 2021-05-01 | CCC | 3 |
| 2021-05-02 | AAA | 5 |
| 2021-05-02 | BBB | 7 |
| 2021-05-02 | CCC | 2 |
Now I would like to introduce a column 'rank' which indicates the rank according to the visitors each day. Expected table would look like:
| day | place | visitors | Rank |
------------------------------------------
| 2021-05-01 | AAA | 20 | 1 |
| 2021-05-01 | BBB | 10 | 2 |
| 2021-05-01 | CCC | 3 | 3 |
| 2021-05-02 | AAA | 5 | 2 |
| 2021-05-02 | BBB | 7 | 1 |
| 2021-05-02 | CCC | 2 | 3 |
Populating the data for the new column Rank can be done with a program like (Pseudocode).
for each i_day in all_days:
SELECT
ROW_NUMBER () OVER (ORDER BY `visitors` DESC) Day_Rank, place
FROM mytable
WHERE `day` = 'i_day'
for each i_place in all_places:
UPDATE mytable
SET rank= Day_Rank
WHERE `Day`='i_day'
AND place = 'i_place'
Since this line by line update is quite inefficient, I'm searching how to optimize this with a SQL sub query in combination with the UPDATE.
(does not work so far...)
for each i_day in all_days:
UPDATE mytable
SET rank= (
SELECT
ROW_NUMBER () OVER (ORDER BY `visitors` DESC) Day_Rank
FROM mytable
WHERE `day` = 'i_day'
)
Typically, this can be done with a subquery that counts the number of rows with visitors greater than the value of visitors of the current row:
UPDATE mytable
SET Day_Rank = (
SELECT COUNT(*) + 1
FROM mytable m
WHERE m.day = mytable.day AND m.visitors > mytable.visitors
);
Note that the result is actually what RANK() would return, if there are ties in the values of visitors.
See the demo.
Or, you could calculate the rankings with ROW_NUMBER() in a CTE and use it in a subquery:
WITH cte AS (
SELECT *, ROW_NUMBER() OVER (PARTITION BY day ORDER BY visitors DESC) rn
FROM mytable
)
UPDATE mytable
SET Day_Rank = (SELECT rn FROM cte c WHERE (c.day, c.place) = (mytable.day, mytable.place));
See the demo.
Or, if your versipn of SQLite is 3.33.0+ you can use the join-like UPDATE...FROM... syntax:
UPDATE mytable AS m
SET Day_Rank = t.rn
FROM (
SELECT *, ROW_NUMBER() OVER (PARTITION BY day ORDER BY visitors DESC) rn
FROM mytable
) t
WHERE (t.day, t.place) = (m.day, m.place);

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

SQLite take N rows per each group

I have an SQLite table similar to the following:
| A | B |
_________
| e | 5 |
| f | 7 |
| a | 5 |
| n | 7 |
| g | 5 |
| d | 7 |
| i | 5 |
| j | 5 |
| e | 7 |
| v | 7 |
How can I retrieve three random rows with value 5 in column B and three random rows with value 7? I don't know values in B, neither values5 ad 7. I want 3 random rows for each different value in B. Result may be not grouped by column B values. It could be something like:
| A | B |
_________
| e | 5 |
| g | 5 |
| e | 7 |
| v | 7 |
| j | 5 |
| f | 7 |
The following almost does what you want:
select t.*
from t
where t.rowid in (select t2.rowid
from t t2
where t2.b = t.b
order by random()
limit 3
);
Alas, the subquery will be run for every row, so this is only approximate because the random number generator changes values on each execution.
One solution is to use a temporary table to store a random number for each row, which can then be used for sorting. Unfortunately, a CTE doesn't seem to do the trick, because these are re-evaluated on each reference.
After some thought, I think a temporary table might be the only solution:
drop table if exists tempt;
create temporary table tempt as
select t.*, random() as rand
from t;
select t.*
from tempt t
where t.rowid in (select t2.rowid
from tempt t2
where t2.b = t.b
order by rand
limit 3
);
You can use the hidden RowID column to get three rows per B value as follows:
SELECT A, B FROM T T1
WHERE RowID IN (SELECT RowID FROM T T2 WHERE B = T1.B LIMIT 3);
Note that you're likely (but not 100% guaranteed) to get the same three rows each time. If you want to get random rows at the expense of some performance, you can do:
SELECT A, B FROM T T1
WHERE RowID IN (SELECT RowID FROM T T2 WHERE B = T1.B ORDER BY random() LIMIT 3);

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