Table rental has values (ID, odo_out, date),
table vehicle has values (ID, odo, car),
both with more columns but not relevant to this.
I have attempted to create a trigger:
CREATE TRIGGER odo_update AFTER INSERT ON rental
BEGIN
UPDATE rental SET odo_out = (SELECT Vehicle.odo FROM Vehicle WHERE rental.ID = Vehicle.ID)
WHERE EXISTS (SELECT * FROM Vehicle WHERE Vehicle.ID = rental.ID);
END;
which should detect a NULL for rental.odo_out and replace it with the value in Vehicle.odo for corresponding ID. This does work, but it updates every row in table, whereas I want it to ONLY update the row with NULL, ie the new row being inserted. An ID can be repeated multiple times in the rental table. How can I do this?
You must set the condition so that only the new row is updated.
This is where you need the keyword NEW to refer to the columns of the new row:
CREATE TRIGGER odo_update AFTER INSERT ON rental
WHEN NEW.odo_out IS NULL
BEGIN
UPDATE rental
SET odo_out = (SELECT odo FROM Vehicle WHERE ID = NEW.ID)
WHERE ID = NEW.ID;
END;
I'm trying to use DB Browser for SQLite to construct a nested query to determine the SECOND highest priced item purchased by the top 10 spenders. The query I have to pick out the top 10 spenders is:
SELECT user_id, max(item_total), SUM (item_total + shipping_cost -
discounts_applied) AS total_spent
FROM orders AS o
WHERE payment_reject = "FALSE"
GROUP BY user_id
ORDER BY total_spent DESC
LIMIT 10
This gives the user_id, most expensive item they purchased (not counting shipping or discounts) as well as the total amount they spent on the site.
I was trying to use a nested query to generate a list of the second most expensive items they purchased, but keep getting errors. I've tried
SELECT user_id, MAX(item_total) AS second_highest
FROM orders
WHERE item_total < (SELECT user_id, SUM (item_total + shipping_cost -
discounts_applied) AS total_spent
FROM orders
WHERE payment_reject = "FALSE"
GROUP BY user_id
ORDER BY total_spent DESC
LIMIT 10)
group by user_id
I keep getting a row value misused error. Does anyone have pointers on this nested query or know of another way to find the second highest item purchased from within the group found in the first query?
Thanks!
(Note: The following assumes you're using Sqlite 3.25 or newer since it uses window functions).
This will return the second-largest item_total for each user_id without duplicates:
WITH ranked AS
(SELECT DISTINCT user_id, item_total
, dense_rank() OVER (PARTITION BY user_id ORDER BY item_total DESC) AS ranking
FROM orders)
SELECT user_id, item_total FROM ranked WHERE ranking = 2;
You can combine it with your original query with something like:
WITH ranked AS
(SELECT DISTINCT user_id, item_total
, dense_rank() OVER (PARTITION BY user_id ORDER BY item_total DESC) AS ranking
FROM orders),
totals AS
(SELECT user_id
, sum (item_total + shipping_cost - discounts_applied) AS total_spent
FROM orders
WHERE payment_reject = 0
GROUP BY user_id)
SELECT t.user_id, r.item_total, t.total_spent
FROM totals AS t
JOIN ranked AS r ON t.user_id = r.user_id
WHERE r.ranking = 2
ORDER BY t.total_spent DESC, t.user_id
LIMIT 10;
Okay, after fixing your table definition to better reflect the values being stored in it and the stated problem, and fixing the data and adding to it so you can actually get results, plus an optional but useful index like so:
CREATE TABLE orders (order_id INTEGER PRIMARY KEY
, user_id INTEGER
, item_total REAL
, shipping_cost NUMERIC
, discounts_applied NUMERIC
, payment_reject INTEGER);
INSERT INTO orders(user_id, item_total, shipping_cost, discounts_applied
, payment_reject) VALUES (9852,60.69,10,0,FALSE),
(2784,123.91,15,0,FALSE), (1619,119.75,15,0,FALSE), (9725,151.92,15,0,FALSE),
(8892,153.27,15,0,FALSE), (7105,156.86,25,0,FALSE), (4345,136.09,15,0,FALSE),
(7779,134.93,15,0,FALSE), (3874,157.27,15,0,FALSE), (5102,108.3,10,0,FALSE),
(3098,59.97,10,0,FALSE), (6584,124.92,15,0,FALSE), (5136,111.06,10,0,FALSE),
(1869,113.44,20,0,FALSE), (3830,129.63,15,0,FALSE), (9852,70.69,10,0,FALSE),
(2784,134.91,15,0,FALSE), (1619,129.75,15,0,FALSE), (9725,161.92,15,0,FALSE),
(8892,163.27,15,0,FALSE), (7105,166.86,25,0,FALSE), (4345,146.09,15,0,FALSE),
(7779,144.93,15,0,FALSE), (3874,167.27,15,0,FALSE), (5102,118.3,10,0,FALSE),
(3098,69.97,10,0,FALSE), (6584,134.92,15,0,FALSE), (5136,121.06,10,0,FALSE),
(1869,123.44,20,0,FALSE), (3830,139.63,15,0,FALSE);
CREATE INDEX orders_idx_1 ON orders(user_id, item_total DESC);
the above query will give:
user_id item_total total_spent
---------- ---------- -----------
7105 156.86 373.72
3874 157.27 354.54
8892 153.27 346.54
9725 151.92 343.84
4345 136.09 312.18
7779 134.93 309.86
3830 129.63 299.26
6584 124.92 289.84
2784 123.91 288.82
1619 119.75 279.5
(If you get a syntax error from the query now, it's because you're using an old version of sqlite that doesn't support window functions.)
I have following two tables:
CREATE TABLE messages (
id integer UNIQUE NOT NULL,
message text,
recipient integer NOT NULL,
sender integer NOT NULL,
sent_at text NOT NULL,
FOREIGN KEY (recipient) REFERENCES users (id),
FOREIGN KEY (sender) REFERENCES users (id)
);
CREATE TABLE users (
id integer UNIQUE NOT NULL,
username text NOT NULL,
);
I need a very specific query, that looks like the following:
SELECT *
FROM messages
WHERE sender = 123 OR recipient = 123
ORDER BY id desc
LIMIT 1
I need to kind of iterate over the messages table, using every user, and putting him in the WHERE statement.
-- TABLE 'users':
-- 123 = id of user1
-- 456 = id of user2
-- 789 = id of user3
Is it possible to iterate in SQLite?
Goal is, to get the newest "conversation" for every user in the users table. For every user, the newest message involving him should be displayed, no matter if that newest message was sent or recieved by him.
You could use a correlated subquery to get that value for each user ID:
SELECT id,
username,
(SELECT MAX(id)
FROM messages
WHERE sender = users.id
OR recipient = users.id
) AS last_message_id
FROM users
This is also possible with GROUP BY.
First join the two table together, then create a group for each user:
SELECT users.id,
MAX(messages.id)
FROM users
JOIN messages ON users.id = messages.sender OR
users.id = messages.recipient
GROUP BY users.id
SELECT year , COUNT(*) AS Count
FROM Movie
WHERE Movie.MID NOT IN
(SELECT DISTINCT m.MID
FROM Movie m
JOIN M_Cast m_c ON m.MID = m_c.MID
JOIN Person p_1 ON m_c.PID = p_1.PID
AND p_1.Gender='Male')
AND Movie.MID IN
(SELECT DISTINCT m.MID
FROM Movie m
JOIN M_Cast m_c ON m.MID = m_c.MID
JOIN Person p_1 ON m_c.PID = p_1.PID
AND p_1.Gender='Female')
GROUP BY year;
The sqlite3 trigger that I want to create might or might not be possible with sql. Five tables are involved:
Members Groups GroupMembers Accounts
mId |name| accId gId | name | accId gId | mId accId | balance
Orders
oId | accId | ammount
When someone deletes a group, I want to make an order for each of the group members with the average of the group balance. So it should do something like this:
CREATE NEW TRIGGER triggername
BEFORE DELETE ON Groups
WHEN ((SELECT balance FROM Accounts WHERE accId=OLD.accId) = 0)
FOR EACH ROW IN
(SELECT accId
FROM GroupMembers JOIN Members ON GroupMembers.mId = Members.mId
WHERE GroupMembers.gId = OLD.gId)
BEGIN
INSERT INTO Orders(accId,ammount) VALUES(accId,
(SELECT balance FROM Accounts WHERE accId = OLD.accId)
/
(SELECT SUM(mId) FROM GroupMembers WHERE gId = OLD.gId)
);
END
The question is: is it possible to create a FOR EACH ROW in any other table than the table at which the trigger applies? Is it possible to put the WHEN statement before the FOR EACH statement?
As documented, there is no such a thing as a FOR EACH ROW IN ... clause.
However, the INSERT statement can use a SELECT statement as the source of the data to be inserted.
Write a SELECT statement that returns one row for each group member:
CREATE TRIGGER triggername
AFTER DELETE ON Groups
FOR EACH ROW
BEGIN
INSERT INTO Orders(accId, amount)
SELECT accId,
(SELECT balance
FROM Accounts
WHERE accId = OLD.accId) /
(SELECT COUNT(*)
FROM GroupMembers
WHERE gId = OLD.gId)
FROM Members
WHERE mId IN (SELECT mId
FROM GroupMembers
WHERE gId = OLD.gId);
END;
(I dropped the balance = 0 filter.)
Consider the following reviews table contents:
CustomerName ReviewDT
Doe,John 2011-06-20 10:13:24
Doe,John 2011-06-20 10:54:45
Doe,John 2011-06-20 11:36:34
Doe,Janie 2011-06-20 05:15:12
The results are ordered by ReviewDT and grouped by CustomerName, such as:
SELECT
CustomerName,
ReviewDT
FROM
Reviews
WHERE
CustomerName NOT NULL
ORDER BY CustomerName ASC, ReviewDT ASC;
I'd like to create a column of the time difference between each row of this query for each Customer... rowid gives the original row, and there is no pattern to the inclusion from the rowid etc...
For the 1st entry for a CustomerName, the value would be 0. I am asking here incase this is something that can be calculated as part of the original query somehow. If not, I was planning to do this by a series of queries - initially creating a new TABLE selecting the results of the query above - then ALTERING to add the new column and using UPDATE/strftime to get the time differences by using rowid-1 (somehow)...
To compute the seconds elapsed from one ReviewDT row to the next:
SELECT q.CustomerName, q.ReviewDT,
strftime('%s',q.ReviewDT)
- strftime('%s',coalesce((select r.ReviewDT from Reviews as r
where r.CustomerName = q.CustomerName
and r.ReviewDT < q.ReviewDT
order by r.ReviewDT DESC limit 1),
q.ReviewDT))
FROM Reviews as q WHERE q.CustomerName NOT NULL
ORDER BY q.CustomerName ASC, q.ReviewDT ASC;
To get the DT of each ReviewDT and its preceding CustomerName row:
SELECT q.CustomerName, q.ReviewDT,
coalesce((select r.ReviewDT from Reviews as r
where r.CustomerName = q.CustomerName
and r.ReviewDT < q.ReviewDT
order by r.ReviewDT DESC limit 1),
q.ReviewDT)
FROM Reviews as q WHERE q.CustomerName NOT NULL
ORDER BY q.CustomerName ASC, q.ReviewDT ASC;