SQLite Nested Query for maximum - sqlite
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.)
Related
unique one column adn return all data with mariaDB [duplicate]
My database structure contains columns: id, name, value, dealer. I want to retrieve row with lowest value for each dealer. I've been trying to mess up with MIN() and GROUP BY, still - no solution.
Solution1: SELECT t1.* FROM your_table t1 JOIN ( SELECT MIN(value) AS min_value, dealer FROM your_table GROUP BY dealer ) AS t2 ON t1.dealer = t2.dealer AND t1.value = t2.min_value Solution2 (recommended, much faster than solution1): SELECT t1.* FROM your_table t1 LEFT JOIN your_table t2 ON t1.dealer = t2.dealer AND t1.value > t2.value WHERE t2.value IS NULL This problem is very famous, so there is a special page for this in Mysql's manual. Check this: Rows Holding the Group-wise Maximum/Minimum of a Certain Column
select id,name,MIN(value) as pkvalue,dealer from TABLENAME group by id,name,dealer; here you group all rows by id,name,dealer and then you will get min value as pkvalue.
SELECT MIN(value),dealer FROM table_name GROUP BY dealer;
First you need to resolve the lowest value for each dealer, and then retrieve rows having that value for a particular dealer. I would do this that way: SELECT a.* FROM your_table AS a JOIN (SELECT dealer, Min(value) AS m FROM your_table GROUP BY dealer) AS b ON ( a.dealer= b.dealer AND a.value = b.m )
Try following: SELECT dealer, MIN(value) as "Lowest value" FROM value GROUP BY dealer;
select id, name, value, dealer from yourtable where dealer in(select min(dealer) from yourtable group by name, value)
These answers seem to miss the edge case of having multiple minimum values for a dealer and only wanting to return one row. If you want to only want one value for each dealer you can use row_number partition - group - the table by dealer then order the data by value and id. we have to make the assumption that you will want the row with the smallest id. SELECT ord_tbl.id, ord_tbl.name, ord_tbl.value, ord_tbl.dealer FROM (SELECT your_table.*, ROW_NUMBER() over (PARTITION BY dealer ORDER BY value ASC, ID ASC) FROM your_table ) AS ord_tbl WHERE ord_tbl.ROW_NUMBER = 1; Be careful though that value, id and dealer are indexed. If not this will do a full table scan and can get pretty slow...
SQLite: Running balance with an ending balance
I have an ending balance of $5000. I need to create a running balance, but adjust the first row to show the ending balance then sum the rest, so it will look like a bank statement. Here is what I have for the running balance but how can I adjust row 1 to not show a sum of the first row, but the ending balance instead. with BalBefore as ( select * from transactions where ACCT_NAME = 'Real Solutions' ORDER BY DATE DESC ) select DATE, amount, '$' || printf("%.2f", sum(AMOUNT) over (order by ROW_ID)) as Balance from BalBefore; This gives me" DATE AMOUNT BALANCE 9/6/2019 -31.00 $-31.00 <- I need this balance to be replaced with $5000 and have the rest 9/4/2019 15.00 $-16.00 sum as normal. 9/4/2019 15.00 $-1.00 9/3/2019 -16.00 $-17.00 I have read many other questions, but I couldn't find one that I could understand so I thought I would post a simpler question.
The following is not short and sweet, but using the WITH statement and CTEs, I hope that the logic is apparent. Multiple CTEs are defined which refer to each other to make the overall query more readable. Altogether the goal was just to add a beginning balance record that could be : /* DROP TABLE IF EXISTS data; CREATE temp TABLE data ( id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, date DATETIME NOT NULL, amount NUMERIC NOT NULL ); INSERT INTO data (date, amount) VALUES ('2019-09-03', -16.00), ('2019-09-04', 15.00), ('2019-09-04', 15.00), ('2019-09-06', -31.00) ; */ WITH initial_filter AS ( SELECT id, date, amount FROM data --WHERE ACCT_NAME = 'Real Solutions' ), prepared AS ( SELECT * FROM initial_filter UNION ALL SELECT 9223372036854775807 as id, --largest signed integer (SELECT MAX(date) FROM initial_filter) AS FinalDate, -(5000.00) --ending balance (negated for summing algorithm) ), running AS ( SELECT id, date, amount, SUM(-amount) OVER (ORDER BY date DESC, id DESC RANGE UNBOUNDED PRECEDING EXCLUDE CURRENT ROW) AS balance FROM prepared ORDER BY date DESC, id DESC ) SELECT * FROM running WHERE id != 9223372036854775807 ORDER BY date DESC, id DESC; This produces the following id date amount balance 4 2019-09-06 -31.00 5000 3 2019-09-04 15.00 5031 2 2019-09-04 15.00 5016 1 2019-09-03 -16.00 5001 UPDATE: The first query was not producing the correct balances. The beginning balance row and the windowing function (i.e. OVER clause) were updated to accurately sum over the correct amounts. Note: The balance on each row is determined completely from the previous rows, not from the current row's amount, because this works backward from an ending balance, not forward from the previous row balance.
ga:itemQuantity in ga_sessions_YYYMMDD (Big Query)
I'm trying to replicate the GA Quantity metric (ga:itemQuantity) using standardSQL and querying the GA export to BigQuery date partitioned tables (ga_sessions_YYYYMMDD). I have tried the following, but 'quantity' is always null: #standardSQL SELECT sum(hit.item.itemQuantity) as quantity FROM `precise-armor-133520.1500218.ga_sessions_20170801` t CROSS JOIN UNNEST(t.hits) AS hit order by 1 ASC; Other metrics work and match 100% with the GA UI so I am assuming it's not a data export problem. For example: SELECT sum( totals.totalTransactionRevenue ) as revenue, sum( totals.transactions ) as transactions FROM `precise-armor-133520.1500218.ga_sessions_201708*` t CROSS JOIN UNNEST(t.hits) AS hit group by `date` order by `date` asc These totals match Revenue and Transactions (metrics) in GA UI respectively. What is the standardSQL query for the GA metric quantity (ga:itemQuantity)?
In order to match "Quantity" in GA's web UI by each date, use the following standard SQL: SELECT SUM(product.productQuantity) ,`date` FROM `precise-armor-133520.1500218.ga_sessions_*` ,UNNEST(hits) AS hits ,UNNEST(hits.product) AS product WHERE hits.eCommerceAction.action_type = "6" and _TABLE_SUFFIX between '20170801' and FORMAT_DATE("%Y%m%d", CURRENT_DATE) group by 2 order by 2 asc
Does this work? #standardSQL SELECT sku, SUM(qtd) qtd FROM( SELECT ARRAY(SELECT AS STRUCT productSKU sku, productQuantity qtd FROM UNNEST(hits), UNNEST(product) WHERE ecommerceAction.action_type = '6') data FROM `precise-armor-133520.1500218.ga_sessions_20170801` ), UNNEST(data) GROUP BY sku ORDER BY qtd DESC LIMIT 1000 Not sure how you managed to unnest the product fields, maybe this solves your issue.
Join two tables in SQLite and Count
I have two tables named "likes" and "comments" and I want to have a table which has counts of likes and comments for each specific user, I wrote following query in SQLite but result is not true for all users, count values for users in both tables are multiple of number of likes and number of comments. SELECT likes.liker_name, likes.liker_id, likes.profile_picture , COUNT(comments.commenter_name) AS comment_count, COUNT( likes.liker_id) AS like_count FROM likes LEFT JOIN comments ON likes.liker_name = comments.commenter_name GROUP BY likes.liker_name ORDER BY COUNT( likes.liker_id) DESC How can I get correct value of count for users that exist in both tables?
The problem is: Some users have comments but no likes, others have likes but no comments, some have both and some have none. Therefore I suggest using a union query and summing that one again SELECT u.name, u.id, u.profile_picture, SUM(u.like_count) AS like_count, SUM(u.comment_count) AS comment_count FROM ( SELECT liker_name AS name, liker_id AS id, profile_picture, COUNT(*) AS like_count, 0 AS comment_count FROM likes GROUP BY liker_name, liker_id, profile_picture UNION ALL SELECT commenter_name AS name, commenter_id AS id, profile_picture, 0 AS like_count, COUNT(*) AS comment_count FROM comments GROUP BY commenter_name, commenter_id, profile_picture ) AS u GROUP BY u.name, u.id, u.profile_picture If you have a separate user table you could also left join the likes count and the comments count subqueries to the user table SELECT u.name, u.id, u.profile_picture, l.cnt AS like_count, c.cnt AS comment_count FROM users u LEFT JOIN (SELECT liker_id, COUNT(*) AS cnt FROM likes GROUP BY liker_id ) AS l ON u.user_id = l.liker_id LEFT JOIN (SELECT commenter_id, COUNT(*) AS cnt FROM comments GROUP BY commenter_id ) AS c ON u.user_id = c.commenter_id WHERE l.cnt > 0 OR c.cnt > 0 No matter how you make it, you must count the comments and the likes in separate subqueries. If you count after joining you are summing on a result where records might be duplicated (the ones on the left side) and you are getting the wrong count.
Time Difference between query result rows in SQLite: How To?
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;