sqlite include 0 in count - sqlite

I've got two tables in a SQLite database, and I'm attempting to calculate the count of the routes by rating. It works, but doesn't return 0 for when there isn't a route with that rating. An example rating would be 8, or 11b, or V3.
The query I'm using right now is:
select routes.rating, count(routes.rating) from routes
left join orderkeys on routes.rating = orderkeys.rating
group by routes.rating
order by orderkeys.key
This doesn't return 0 for the ratings that don't have any routes for them, though. The output I get is:
10d|3
7|3
8|2
9|9
10a|5
10b|4
10c|2
11a|3
12b|1
V0|5
V1|7
V2|5
V3|8
V4|3
V5|1
V6|2
V7|3
V8|2
V9|1
What I expect to get is:
7|3
8|2
9|9
10a|5
10b|4
10c|2
10d|3
11a|3
11b|0
11c|0
11d|0
12a|0
12b|1
12c|0
12d|0
V0|5
V1|7
V2|5
V3|8
V4|3
V5|1
V6|2
V7|3
V8|2
V9|1
Here's the schema:
CREATE TABLE routes (
id integer PRIMARY KEY,
type text, rating text,
rope integer,
name text,
date text,
setter text,
color_1 text,
color_2 text,
special_base text,
blurb text,
updater text
);
CREATE TABLE orderkeys (
rating TEXT,
key INTEGER PRIMARY KEY
);

A left join returns all records from the left table, but what you want is all ratings, i.e., all records from the orderkeys table:
SELECT orderkeys.rating,
COUNT(routes.id)
FROM orderkeys
LEFT JOIN routes USING (rating)
GROUP BY orderkeys.rating
ORDER BY orderkeys.key

Try this. I do not like join quite much but it is quite useful when there are a lot of tables:
select routes.rating, count(routes.rating) from routes, rating
where routes.rating = orderkeys.rating
group by routes.rating
order by orderkeys.key

Related

Insert multiple values with the same foreign key

I have two tables that reference each other:
CREATE TABLE Room
room_id INTEGER PRIMARY KEY,
room_name TEXT UNIQUE NOT NULL;
CREATE TABLE Item
item_id INTEGER PRIMARY KEY,
room_id INTEGER,
item_name TEXT,
FOREIGN KEY (room_id) REFERENCES Room (room_id) ON UPDATE RESTRICT ON DELETE RESTRICT;
Now I want to add a new room and add a few dozen items to go into it.
INSERT INTO Room(room_name) VALUES ('Living Room');
Let's say I don't know how many rooms there are, and I just want to put stuff into the living room. To do that, I need to select the right room_id. For a single item this is not too bad:
INSERT INTO Item(room_id, item_name)
SELECT room_id, 'Couch' AS item_name FROM Room WHERE room_name = 'Living Room';
But what if I want to insert a bunch of values simultaneously. I tried using last_insert_rowid, but that does not treat the entire INSERT as a single transaction. In other words, the last ID keeps incrementing
INSERT INTO Item (room_id, item_name)
VALUES
(last_insert_rowid(), 'Chair'),
(last_insert_rowid(), 'TV'),
(last_insert_rowid(), 'Carpet');
I would like to avoid having to use the SELECT on each new row. Is there a way to insert multiple values into Item, while referencing the last known room_id in Room?
Something in the nature of a CROSS JOIN would likely be very useful, but I don't know how to get the constants to behave in that case
The end result I am looking for is for Room to look like this:
room_id | room_name
--------+-----------
1 | Living Room
And Item like this:
item_id | room_id | item_name
--------+---------+-----------
1 | 1 | Chair
2 | 1 | TV
3 | 1 | Carpet
You can use a CROSS join of the id that you get from the new room to a CTE that returns the items that you want to insert:
WITH cte(item_name) AS (VALUES ('Chair'), ('TV'), ('Carpet'))
INSERT INTO Item (room_id, item_name)
SELECT r.room_id, c.item_name
FROM Room r CROSS JOIN cte c
WHERE r.room_name = 'Living Room';
See the demo.
If you are using a version of SQLite that does not support CTEs use UNION ALL in a subquery:
INSERT INTO Item (room_id, item_name)
SELECT r.room_id, c.item_name
FROM Room r
CROSS JOIN (
SELECT 'Chair' item_name UNION ALL
SELECT 'TV' UNION ALL
SELECT 'Carpet'
) c
WHERE r.room_name = 'Living Room';
See the demo.

Selecting all max values of column for each distinct value of other column

I am trying to get a list of most used tags for posts on a website on a given day. I currently have this query:
SELECT posts.pdate, tags.tag, count(posts.pid) as post_count
FROM posts, tags
WHERE posts.pid = tags.pid
GROUP BY posts.pdate, tags.tag
ORDER BY posts.pdate;
This provides me with each distinct tag, along with the date they are used on as well as how many posts used them, returning me with this:
2020-09-10|CMPUT291|1
2020-09-10|computing|1
2020-09-10|database|2
2020-09-10|frequentTag1|2
2020-09-10|relational|2
2020-09-10|sql|1
2020-09-10|tieTag1|2
2020-09-11|Database|1
2020-09-11|data|1
2020-09-11|relational|1
2020-09-11|sql|1
2020-09-13|Database|1
2020-09-13|Sql language|1
2020-09-13|access|1
2020-09-13|frequentTag3|2
2020-09-13|query|3
2020-09-13|relational|3
2020-09-13|sql|1
2020-09-17|Database|1
2020-09-17|frequentTag3|3
2020-09-17|query|1
2020-09-17|relational|1
2020-09-17|sql|1
2020-09-17|sql language|1
2020-09-20|RELATIONAL|1
2020-09-20|database|1
2020-09-20|query|1
2020-09-20|sql language|1
2020-09-25|database|1
2020-09-25|sql language|1
2020-09-30|boring|2
2020-09-30|extra tag|1
2020-09-30|fun|3
2020-09-30|just here|1
2020-09-30|more tag|1
2020-09-30|sleep|3
2020-09-30|tag tag|1
2020-09-30|tag test|1
2020-09-30|test tag|1
But, I now need to make it only give me the rows that have the max (or all of them with max in case of a tie) for each date.
I WANT to be able to use MAX(count(posts.pid)) but I know that doesn't work so I need to find an alternative.
I should get a final result of this:
2020-09-10|database|2
2020-09-10|frequentTag1|2
2020-09-10|relational|2
2020-09-10|tieTag1|2
2020-09-11|Database|1
2020-09-11|data|1
2020-09-11|relational|1
2020-09-11|sql|1
2020-09-13|query|3
2020-09-13|relational|3
2020-09-17|frequentTag3|3
2020-09-20|RELATIONAL|1
2020-09-20|database|1
2020-09-20|query|1
2020-09-20|sql language|1
2020-09-25|database|1
2020-09-25|sql language|1
2020-09-30|fun|3
2020-09-30|sleep|3
Any help would be greatly appreciated.
APPLICABLE SCHEMA:
create table posts (
pid char(4),
pdate date,
title text,
body text,
poster char(4),
primary key (pid),
foreign key (poster) references users
);
create table tags (
pid char(4),
tag text,
primary key (pid,tag),
foreign key (pid) references posts
);
You can use RANK() window function:
SELECT pdate, tag, post_count
FROM (
SELECT p.pdate,
t.tag,
COUNT(*) post_count,
RANK() OVER (PARTITION BY p.pdate ORDER BY COUNT(*) DESC) rnk
FROM posts p INNER JOIN tags t
ON p.pid = t.pid
GROUP BY p.pdate, t.tag
)
WHERE rnk = 1
ORDER BY pdate, tag;
You should use a proper JOIN with an ON clause instead of that outdated syntax with the WHERE clause.

Complex query on sql lite xcode

I'm developing an iOS app and I have a sqlite database with 2 tables related by 1-to-many relationship.
Now I would like to do a query that retrieve all element by first table and in the same time do a count by second table so I can pass the result into my view.
CREATE TABLE track(
trackid INTEGER,
trackname TEXT,
trackartist INTEGER,
FOREIGN KEY(trackartist) REFERENCES artist(artistid)
);
CREATE TABLE artist(
artistid INTEGER PRIMARY KEY,
artistname TEXT
);
I would like to create a query that returns all artist name and the count of track for each artist name so I can pass this value to my list.
Is it possible? Any help?
Thanks to Joe, your code works well for my, but it's possibile to add new field for store the result of count?
Sorry and if i would take the also all trackname for each artist in the same query?
SELECT a.artistname, count(*)
FROM track t
INNER JOIN artist a
on t.trackartist = a.artistid
GROUP BY a.artistid
Try this:
SELECT a.artistname,
(SELECT COUNT(*) FROM track t
WHERE t.trackartist = a.artistid)
FROM artist a

How to store an element with a lot of fields

I want to design a database system (I use SQLite)and in a table where I keep the history, I store some values of an employee (name,surname, id, etc..) One of the fields are some working positions which currently are 3, but in the future may increased to 4 or 5... Which is is more clever to do?
1) Have a table with all the fields (among them: wp1, wp2, wp3) and later add a column for the wp3, or
2) Store all these working positions to a diferrent table where i will have 2 fields id and wp and store the diferrent wp to multiple records?
Is a 'working position' a job title? A record of employment at a previous company?
1 is a bad idea.
You probably want something like this:
create table employees (
id int primary key,
name text not null
);
create table working_positions (
id int primary key,
employee_id int not null references employees(id), /* foreign key to employees table */
...other attributes of a working position...
);

SQL Select Query Asp.Net

I have a product page on a webpage that shows categories of products. This is done with a listview populated from a database. The issue that I have is that the main supplier has demanded that their products are first in the category list. So what I need to do is run a query that will return the results, display those two categories first and then display the rest alphabetically.
So I've been trying to do this using a UNION ALL query like this:
SELECT cat, cat_id, image FROM prod_categories WHERE cat_id = 19 OR cat_id = 65
UNION ALL
SELECT cat, cat_id, image FROM prod_categories WHERE cat_id <> 19 AND cat_id <> 65
I thought with a union like this it would display the results of the first select query first, but it's not doing that.
I can add an 'order by cat' clause on the end, but obviously that only displays them in the correct order if the two categories I want to display come first alphabetically, which they don't.
If anyone has any ideas how to do this it would be greatly appreciated.
Thanks
How about this:
SELECT cat, cat_id, image FROM prod_categories
order by case when cat_id in (19, 65) then 1 else 2 end, cat_id
Cuts out the need to UNION altogether. Might even produce a more efficient execution plan (possibly...).
(using Transact-SQL for SQL Server - the exact syntax may have to be tinkered for MySql etc)
Try something like this.
SELECT cat, cat_id, image, 1 as [srt]
FROM prod_categories WHERE cat_id = 19 OR cat_id = 65
UNION ALL
SELECT cat, cat_id, image, 2 as [srt]
FROM prod_categories WHERE cat_id <> 19 AND cat_id <> 65
ORDER BY srt ASC, cat_id
Don't hard-code this into your query. What happens when the next supplier wants to come second? Or last? For that matter, you may want to list categories in some sort of "group", anyways.
Instead, you should be using an ordering table (or multiple). Something simple to get you started:
CREATE TABLE Category_Order (categoryId INTEGER -- fk to category.id, unique
priority INTEGER) -- when to display category
Then you want to insert the values for the current "special" categories:
INSERT INTO Category_Order (categoryId, priority) VALUES (19, 2147483647), (65, 0)
You'll also need an entry for rows that are not currently prioritized:
INSERT INTO Category_Order (categoryId, priority)
SELECT catId, -2147483648
FROM prod_categories
WHERE catID NOT IN (19, 65)
Which can then be queried like this:
SELECT cat, cat_id, image
FROM prod_categories
JOIN Category_Order
ON category_id = cat_id
ORDER BY priority DESC, cat
If you write a small maintenance program for this table, you can then push re-ordering duties off onto the correct business department. Reordering of entries can be accomplished by splitting the difference between existing entries, although you'll want a procedure to re-distribute if things get too crowded.
Note that, in the event your db supports a clause like ORDER BY priority NULLS LAST, the entries for non-prioritized categories are unnecessary, and you can simply LEFT JOIN to the ordering table.

Resources