Reordering the results of an SQL query using SQLite - sql

I have a SQLite database that models Sanskrit nouns and has tables like this: (Sorry if it is very lengthy. I've tried to cut things down to the minimum necessary to understand this problem.)
numbers:
id
number
1
singular
2
dual
3
plural
cases:
id
case
1
nominative
2
accusative
3
instrumental
4
dative
5
ablative
6
genitive
7
locative
8
vocative
nouns:
id
name
1
rAma
forms:
id
form
noun
1
rAmaH
1
2
rAmau
1
3
rAmAH
1
4
rAmam
1
5
rAmAN
1
6
rAmENa
1
7
rAmAbhyAm
1
8
rAmaiH
1
9
rAmAya
1
10
rAmebhyaH
1
11
rAmAt
1
12
rAmasya
1
13
ramayoH
1
14
rAmANAm
1
15
rAme
1
16
rAmeShu
1
17
rAma
1
noun is a foreign key which references nouns(id)
nounforms:
id
form
case
number
noun
1
1
1
1
1
2
2
1
2
1
3
3
1
3
1
4
4
2
1
1
5
2
2
2
1
6
5
2
3
1
7
6
3
1
1
8
7
3
2
1
9
8
3
3
1
10
9
4
1
1
11
7
4
2
1
12
10
4
3
1
13
11
5
1
1
14
7
5
2
1
15
10
5
3
1
16
12
6
1
1
17
13
6
2
1
18
14
6
3
1
19
15
7
1
1
20
13
7
2
1
21
16
7
3
1
22
17
8
1
1
23
2
8
2
1
24
3
8
3
1
form is a foreign key which references forms(id)
case is a foreign key which references cases(id)
number is a foreign key which references numbers(id)
noun is a foreign key which references nouns(id)
I can get all the declensions of the noun rAma with this SQL query:
SELECT forms.form FROM forms JOIN nouns,nounforms
WHERE forms.id = nounforms.form
AND nounforms.noun = nouns.id
AND noun.name = "rAma"
GROUP BY nounforms.case, nounforms.number;
and that returns the whole noun perfectly in 24 rows:
form
rAmaH
rAmau
rAmAH
rAmam
rAmau
rAmAN
rAmENa
rAmAbhyAm
rAmaiH
rAmAya
rAmAbhyAm
rAmebhyaH
rAmAt
rAmAbhyAm
rAmebhyaH
rAmasya
ramayoH
rAmANAm
rAme
ramayoH
rAmeShu
rAma
rAmau
rAmAH
So far so good. But what I would really like is something like this:
singular
dual
plural
rAmaH
rAmau
rAmAH
rAmam
rAmau
rAmAN
rAmENa
rAmAbhyAm
rAmaiH
rAmAya
rAmAbhyAm
rAmebhyaH
rAmAt
rAmAbhyAm
rAmebhyaH
rAmasya
ramayoH
rAmANAm
rAme
ramayoH
rAmeShu
rAma
rAmau
rAmAH
i.e. 8 rows for each case with 3 columns for each number. The problem is my SQL knowledge is not quite enough to get me there. I think what I want is a view or a virtual table. Is that right? Also once that is solved, I would like to parametrize the query so I can use it for nouns other than rAma but SQLite does not I believe support stored procedures. Is that right? If so, what is the workaround?
Btw, I am aware that I can do the reordering in my application. In fact, that is what I am
doing now but I would like to keep as much centralized in the database as possible so I can port to other languages/environments.
Can anyone help?

You need conditional aggregation:
SELECT MAX(CASE WHEN nf.number = 1 THEN f.form END) singular,
MAX(CASE WHEN nf.number = 2 THEN f.form END) dual,
MAX(CASE WHEN nf.number = 3 THEN f.form END) plural
FROM forms f
JOIN nouns n ON n.id = f.noun
JOIN nounforms nf ON f.id = nf.form AND nf.noun = n.id
WHERE n.name = ?
GROUP BY nf.`case`;
Replace the placeholder ? with the noun that you want.
Also, always use proper joins with ON clauses and aliases for the tables to make the code shorter and more readable.
See the demo.
As you already know, SQLite does not support stored procedures or functions, so probably the best way to use this query is as it is in your app with the the placeholder ? in a prepared statement and pass the value of the noun as a parameter.

Related

Cant merge two queries with different columns

I'm studying SQL and somehow I'm stuck with a question. I have 2 tables ('users' and 'follows').
Follows Table
user_id
follows
date
1
2
1993-09-01
2
1
1989-01-01
3
1
1993-07-01
2
3
1994-10-10
3
2
1995-03-01
4
2
1988-08-08
4
1
1988-08-08
1
4
1994-04-02
1
5
2000-01-01
5
1
2000-01-02
5
6
1986-01-10
7
1
1990-02-02
1
7
1996-10-01
1
8
1993-09-03
8
1
1995-09-01
8
9
1995-09-01
9
8
1996-01-10
7
8
1993-09-01
3
9
1996-05-30
4
9
1996-05-30
Users Table
user_id
first_name
last_name
school
1
Harry
Potter
Gryffindor
2
Ron
Wesley
Gryffindor
3
Hermonie
Granger
Gryffindor
4
Ginny
Weasley
Gryffindor
5
Draco
Malfoy
Slytherin
6
Tom
Riddle
Slytherin
7
Luna
Lovegood
Ravenclaw
8
Cho
Chang
Ravenclaw
9
Cedric
Diggory
Hufflepuff
I need to list all rows from follows where someone from one house follows someone from a different house. I tried to make 2 queries, one to get all houses related to follows.user_id and another one with all houses related to follows.follows and "merge" then:
select a.nome_id, a.user_id_house, b.follows_id, b.follows_house
from ( select follows.user_id as nome_id
, users.house as user_id_house
from follows inner join users
ON users.user_id = follows.user_id
) as a,
( select follows.follows as follows_id
, users.house as follows_house
from follows inner join users
ON follows.user_id = users.user_id
) as b
where a.user_id_house <> b.follows_house;
The problem is that the result is like 400 rows, its not right. I have no idea how I could solve this.
Try this
SELECT follows.user_id, users.school, followers.user_id, followers.school FROM follows
JOIN users ON follows.user_id=users.user_id
JOIN users as followers ON follows.follows=followers.user_id
WHERE users.school <> followers.school
Note: Pay attention to naming in my answer
Thanks for correcting to Thorsten Kettner

reorder sort_order in table with sqlite

I have this table:
id sort_ord
0 6
1 7
2 2
3 3
4 4
5 5
6 8
Why does this query:
UPDATE table
SET sort_ord=(
SELECT count(*)
FROM table AS pq
WHERE sort_ord<table.sort_ord
ORDER BY sort_ord
)
WHERE(sort_ord>=0)
Produce:
id sort_ord
0 4
1 5
2 0
3 1
4 2
5 4
6 6
I was expecting all sort_ord fields to subtract by 2.
Here is defined: https://www.sqlite.org/isolation.html
About this link i can interpret, you has several instances for one query (update table and select count table) and independent of each other.
When you are in update sort_data(5) id 5, you have new data for read on every "SET sot_ord" (understanding what say about isolation), and now the result is 4.
Every select is a new instance and a new data reading
id sort_ord
0 4
1 5
2 0
3 1
4 2
5 5**
6 8**

SQL: Create view from 2 tables printing null values when no records

I have in my DB these 2 tables:
LESSONS RATINGS
ID | NAME ID | LESSON | RATING
1 lesson1 1 1 4
2 lesson2 2 2 2
3 lesson3 3 1 5
4 lesson4 4 4 2
5 lesson5 5 3 1
6 lesson6 6 2 5
7 lesson7 7 6 3
And I want a View that show me something like this:
LESSONS_RATINGS
IDL| NAME | RATING
1 lesson1 4.5
2 lesson2 3.5
3 lesson3 1
4 lesson4 2
5 lesson5 NULL
6 lesson6 3
7 lesson7 NULL
But what I've been able to get so far is this:
LESSONS_RATINGS
IDL| NAME | RATING
1 lesson1 4.5
2 lesson2 3.5
3 lesson3 1
4 lesson4 2
6 lesson6 3
Notice that NULL records are missing. That's why in table RATINGS there are no records of lessons 5 and 7. I'm doing this:
CREATE OR REPLACE VIEW `LESSONS_RATINGS` AS
select
`l`.`ID` AS `IDL`,
`l`.`NAME` AS `NAME`,
CASE WHEN AVG(`lr`.`RATING`) IS NULL THEN NULL ELSE AVG(`lr`.`RATING`) END AS `RATING`
from
`LESSONS` AS `l`,
`RATINGS` AS `lr`
where
(`l`.`ID` = `lr`.`ID`)
group by `l`.`ID`;
Use an OUTER JOIN:
select
`l`.`ID` AS `IDL`,
`l`.`NAME` AS `NAME`,
AVG(`lr`.`RATING`) AS `RATING`
from
`LESSONS` AS `l` LEFT JOIN `RATINGS` AS `lr`
ON `l`.`ID` = `lr`.`ID`
group by `l`.`ID`;
Also, I don't think there is a need for the Case statement -- you can just use AVG(lr.rating).
A Visual Explanation of SQL Joins

How to find count from two joined tables

We have to find count for each risk category for impact level as shown in last result part
Risk Table
RiskID RiskName
----------------------
1 Risk1
2 Risk2
3 Risk3
4 Risk4
5 Risk5
6 Risk6
7 Risk7
8 Risk8
9 Risk9
10 Risk10
11 Risk11
Category Table
Cat_ID Cat_Name
--------------------------
1 Design
2 Operation
3 Technical
Risk_Category table
Risk_ID Category_ID
------------------------
1 1
1 2
2 1
3 1
3 3
4 1
5 2
6 1
7 3
8 1
9 3
10 3
Risk_Impact_Assessment table
Risk_ID Impact_Level Impact_Score
---------------------------------------------
1 High 20
2 Medium 15
3 High 20
4 Low 10
5 High 20
6 High 20
7 High 20
8 Low 10
9 Medium 15
10 Low 15
11 Medium 15
Result should be like this
Cat_Name Impact_Level_High Impact_Level_Medium Impact_Level_Low
-------------------------------------------------------------------------------------
Design 1 1 2
Operation 2
Technical 2 2 1
You probably want to use the group by clause, along with case, eg.:
select
Cat_Name,
sum(case when Impact_Level = 'High' then 1 else 0 end) as [Impact_Level_High],
sum(case when Impact_Level = 'Medium' then 1 else 0 end) as [Impact_Level_Medium],
sum(case when Impact_Level = 'Low' then 1 else 0 end) as [Impact_Level_Low]
from [Risk_Impact_Assessment]
...
group by Cat_Name;
(I left out all the joins, I assume you can write these no problem)
You can use this trick to accomplish a lot of cool things, including parametric sorting and (just like here) complicated aggregate functions with little work.

SQL - conditional statements in crosstab queries - say what

I am working with MS Access 2007. I have 2 tables: Types of Soda, and Likeability.
Types of Soda are: Coke, Pepsi, Dr. Pepper, and Mello Yellow
Likeability is a lookup with these options: Liked, Disliked, No preference
I know how to count the number of Cokes or Mello Yellows in the table using DCount("[Types]", "[Types of Soda]", "[Types]" = 'Coke')
I also know how to count the number of Liked, Disliked, No preference.
("[Perception]", "[Likeability]", "[Perception]" = 'Liked')
But, what if I need to count the number of "Likes" by Type.
i.e. the table should look like this:
Coke | Pepsi | Dr. Pepper | Mello Yellow
Likes 9 2 12 19
Dislikes 2 45 1 0
No Preference 0 12 14 15
I know in Access I can create a cross tab queries, but my tables are joined by an ID. So my [Likeability] table has an ID column, which is the same as the ID column in my [Types] table. That's the relationship, and that's what connects my tables.
My problem is that I don't know how to apply the condition for counting the likes, dislikes, etc, for ONLY the Types that I specify. It seems like I first have to check the [Likeability] table for "Likes", and cross reference the ID with the ID in the [Types] table.
I am very confused, and you may be too, now. But all I want to do is count the # of Likes and Dislikes for each type of soda.
Please help.
Its not really clear (to me anyway) what your tables look like so lets assume the following
tables
Soda
------
Soda_ID (Long Integer (Increment))
Soda_Name (Text(50)
Perception
------
Perception_ID (Long Integer (Increment))
Perception_Name (Text(50)
Likeability
-----------
Likeability_ID (Long Integer (Increment))
Soda_ID (Long Integer)
Perception_ID (Long Integer)
User_ID (Long Integer)
Data
Soda_Id Soda_Name
------- ---------
1 Coke
2 Pepsi
3 Dr. Pepper
4 Mello Yellow
Perception_ID Perception_Name
------------- ---------
1 Likes
2 Dislikes
3 No Preference
Likeability_ID Soda_ID Perception_ID User_ID
-------------- ------- ------------- -------
1 1 1 1
2 2 1 1
3 3 1 1
4 4 1 1
5 1 2 2
6 2 2 2
7 3 2 2
8 4 2 2
9 1 3 3
10 2 3 3
11 3 3 3
12 4 3 3
13 1 1 5
14 2 2 6
15 2 2 7
16 3 3 8
17 3 3 9
18 3 3 10
Transform query You could write a query like this
TRANSFORM
Count(l.Likeability_ID) AS CountOfLikeability_ID
SELECT
p.Perception_Name
FROM
Soda s
INNER JOIN (Perception p
INNER JOIN Likeability l
ON p.Perception_ID = l.Perception_ID)
ON s.Soda_Id = l.Soda_ID
WHERE
p.Perception_Name<>"No Preference"
GROUP BY
p.Perception_Name
PIVOT
s.Soda_Name;
query output
Perception_Name Coke Dr_ Pepper Mello Yellow Pepsi
--------------- ---- ---------- ------------ -----
Dislikes 1 1 1 3
Likes 2 1 1 1