Count mutiple column values from table - sql

It's a simple table that looks like this:
id TEXT,
name TEXT,
FOREIGN KEY(id) REFERENCES users(id) ON DELETE CASCADE,
PRIMARY KEY(id, name)
What I want is to be able to get a set of rows with counts for all names in that table. Basically how many times each name is present in the table.
This is my code so far and it works:
var names = ['upload_files', 'create_users', 'remove_users', 'edit_tags'];
var query = 'SELECT COUNT(p.id) AS count FROM perms p JOIN users u ON p.id = u.id WHERE p.name = ?',
for(let name of names){
this.db.prepare(query).all([name]).map(value => data[name] = value.count);
}
It produces something like this:
upload_files: 5,
create_users: 2,
remove_users: 2,
edit_tags: 5,
Assuming there are 5 rows with upload_files, 2 rows with remove_users and so on...
But it's very slow when I have 50K records, each query takes 2 seconds.
Can it be combined in a single query? that way I can reduce the time to 2 seconds for all names

Filter the names using IN and group by the name.
SELECT p.name,
count(p.id) count
FROM perms p
WHERE p.name IN ('upload_files',
'create_users',
'remove_users',
'edit_tags')
GROUP BY p.name;
And consider to try if an index on name speeds things up.

Related

How to aggregate based on other rows that share a key?

I have a table in the following format:
I feel like this should be simple but I'm struggling to come up with a performant query that can perform aggregations based on other rows with a shared key. For example, I want to sum the rows for a user with the key MediaLength but only if the rows with the key Score that share the event_id are greater than or equal to 3.
The result from a simple sum:
SELECT SUM(value::float) FROM data WHERE key = 'MediaLength' AND user_id = '9765f312-0d0b-4db0-b4c5-217eec81d7c3'
Result: 40
The result I am trying to achieve here is 15. In the table above you can see the rows are children of an event. I only want to sum the value column where key = 'MediaLength' and its sister row with key = 'Score' has value >= 3.
This is the query I have tried so far but it seems a bit messy and also doesn't work due to a more than one row returned by subquery error:
select
sum(value::float)
filter (where (
select d.value::float
from data d
where d.event_id = event_id
and d.key = 'Score'
) >= 3)
from data
where user_id = '9765f312-0d0b-4db0-b4c5-217eec81d7c3'
This is a simple example but in the future I would need to filter on potentially multiple other keys as well, so any advice on how to extend that is also hugely appreciated.
I only want to sum the value column where key = 'MediaLength' and its sister row with key = 'Score' has value >= 3.
SELECT sum(value::float) -- why the cast?
FROM data d
WHERE user_id = '9765f312-0d0b-4db0-b4c5-217eec81d7c3'
AND key = 'MediaLength'
AND EXISTS (
SELECT FROM data ds
WHERE ds.event_id = d.event_id
AND ds.user_id = d.user_id -- !
AND ds.key = 'Score'
AND ds.value >= 3
);
Here, rows with key = 'MediaLength' qualify if any sister passes the filter. (There may be more sisters failing the test.)
If there can only ever be a single qualifying sister row (enforced by a unique constraint / index?), a self-join is a bit simpler:
SELECT sum(value::float)
FROM data d
JOIN data ds USING (event_id, user_id)
WHERE d.user_id = '9765f312-0d0b-4db0-b4c5-217eec81d7c3'
AND d.key = 'MediaLength'
AND ds.key = 'Score'
AND ds.value >= 3;
The self-join would produce multiple result rows for multiple qualifying sister rows.
At its core, this can be cast as relational-division problem. Especially, since ...
in the future I would need to filter on potentially multiple other keys as well
See:
How to filter SQL results in a has-many-through relation

Getting values from two tabels which are connected via a third table in PostgreSQL

There are three tables in my DB named questions, question_video, and question_video_progress.
question_video and question_video_progress has a foreign key ques_id which is the primary key of questions.
What I want is a query with which I can get video_id from the question_video table and video_progress_time from question_video_table corresponding to some ques_id and email respectively.
There can be a case where a ques_id corresponds to no video_id in question_video.
And a case where a ques_id does not correspond to a video_progress_time.
SELECT
QUESTION_VIDEO.VIDEO_ID,
QUESTION_VIDEO_PROGRESS.VIDEO_PROGRESS_TIME
FROM QUESTION_VIDEO
RIGHT JOIN QUESTIONS ON QUESTION_VIDEO.QUES_ID = QUESTIONS.QUES_ID
LEFT JOIN QUESTION_VIDEO_PROGRESS ON QUESTION_VIDEO_PROGRESS.QUES_ID = QUESTIONS.QUES_ID
WHERE
(QUESTION_VIDEO_PROGRESS.EMAIL = 'someEmail#gmail.com' AND QUESTION_VIDEO.QUES_ID = 2)
OR
QUESTION_VIDEO.QUES_ID = 2;
The above query returns two rows corresponding to ques_id. What I want is if there is an email corresponding to a ques_id in QUESTION_VIDEO_PROGRESS table then return that email-specific value else just return video_id corresponding to the ques_id if that email does not exists.
I think you just want:
select qv.video_id, qvp.video_progress_time
from question_video qv
left join question_video_progress qvp
on qvp.ques_id = q.ques_id
and qvp.email = 'someEmail#gmail.com'
where qv.ques_id = 2
The important point is to put the condition on the left joined table in the on clause of the join, so rows without a match are not filtered out.
Also, note that you don't need to bring table questions to produce the result that you want.

SQL - Query that returns the Username along with their total count of records

I'm new to the relational database stuff and Im having a hard time understanding how to write a query to do what I want. I have two tables that have a relationship.
CREATE TABLE DocumentGroups (
id INTEGER PRIMARY KEY AUTOINCREMENT,
comments TEXT,
Username TEXT NOT NULL,
)
CREATE TABLE Documents (
id INTEGER PRIMARY KEY,
documentGroupId INT NOT NULL,
documentTypeId INT NOT NULL,
documentTypeName TEXT NOT NULL,
succesfullyUploaded BIT
)
I would like to query the Documents table and get the record count for each username. Here is the query that I came up with:
SELECT Count(*)
FROM DOCUMENTS
JOIN DocumentGroups ON Documents.documentGroupId=DocumentGroups.id
GROUP BY Username
I currently have 2 entries in the Documents table, 1 from each user. This query prints out:
[{Count(*): 1}, {Count(*): 1}]
This looks correct, but is there anyway for me to get he username associated with each count. Right now there is no way of me knowing which count belongs to each user.
You are almost there. Your query already produces one row per user name (that's your group by clause). All that is left to do is to put that column in the select clause as well:
select dg.username, count(*) cnt
from documents d
join documentgroups dg on d.documentgroupid = dg.id
group by dg.username
Side notes:
table aliases make the queries easier to read and write
in a multi-table query, always qualify all columns with the (alias of) table they belong to
you probably want to alias the result of count(*), so it is easier to consume it from your application

Json query vs SQL query using JSON in Oracle 12c (Performance)

I am using oracle 12c and Sql Developer with json
For this example I have the follow JSON:
{
"id": "12",
"name": "zhelon"
}
So I have created the follow table for this:
create table persons
id number primary key,
person clob,
constraint person check(person is JSON);
The idea is persist in person column the previous JSON and use a the follow query to get that data
SELECT p.person FROM persons p WHERE json_textvalue('$name', 'zhelon')
Talking about perfonce, I am intresting to extract some json field and add new a colum to the table to improve the response time (I don't know if that is possible)
create table persons
id number primary key,
name varchar(2000),
person clob,
constraint person check(person is JSON);
To do this:
SELECT p.person FROM persons p WHERE p.name = 'zhelon';
My question is:
What's the best way to make a query to get data? I want to reduce the response time.
Which query get the data faster ?
SELECT p.person FROM persons p WHERE json_textvalue('$name', 'zhelon')
or
SELECT p.person FROM persons p WHERE p.name = 'zhelon';
You can create a virtual column like this:
ALTER TABLE persons ADD (NAME VARCHAR2(100)
GENERATED ALWAYS AS (JSON_VALUE(person, '$name' returning VARCHAR2)) VIRTUAL);
I don't know the correct syntax of JSON_VALUE but I think you get an idea.
If needed you can also define a index on such columns like any other column.
However, when you run SELECT p.person FROM persons p WHERE p.name = 'zhelon';
I don't know which value takes precedence, p.person from JSON or the column.
Better use a different name in order to be on the safe side:
ALTER TABLE persons ADD (NAME_VAL VARCHAR2(100)
GENERATED ALWAYS AS (JSON_VALUE(person, '$name' returning VARCHAR2)) VIRTUAL);
SELECT p.person FROM persons p WHERE p.NAME_VAL= 'zhelon';

SQL: List all entries for a foreign key, acquired in same query

I need to make a really simple DB in an android application (I'm using SQLite) and have no idea how to make this query.
Let's say I have these two tables, linked together with the locationid from the first:
CREATE TABLE locations
(
locationid INTEGER PRIMARY KEY AUTO_INCREMENT,
floor INTEGER,
room INTEGER
);
CREATE TABLE entries
(
entryid INTEGER PRIMARY KEY AUTO_INCREMENT,
title TEXT,
summary TEXT,
date DATE,
FOREIGN KEY(location) REFERENCES locations(locationid)
);
How do I construct the following query: get all rows from table "entries" for room 308, floor 3?
SELECT *
FROM entries
WHERE locations = (SELECT locationid
FROM locations
WHERE room=308
AND floor=3)
Alternatively:
SELECT e.*
FROM entries e
JOIN locations l ON e.locations = l.locationid
WHERE l.room = 308
AND l.floor = 3
SELECT e.*
FROM locations l INNER JOIN entries e
ON l.locationid = e.location
WHERE l.floor = 3
AND l.room = 308
It's a simple JOIN statement :
select
locations.*, -- suppress this if only entries table are needed
entries.* -- you can select only date and title instead of everything
from locations
join entries
on entries.location=locations.locationid -- using your foreign key
where
locations.room=308 -- first condition
and locations.floor=3 -- second condition
;
You could use LEFT JOIN instead of JOIN if you want to retrieve informations on the room (for example) even if no data are linked within entries table.
Final word : I'm pretty sure the answer was already here on stackoverflow and on the web too.