Sort by id desc on multiple columns distinct postrges - sql

SELECT impressions.*
FROM impressions
WHERE impressions.user_id = 2
AND impressions.action_name = 'show'
AND (impressions.message IS NOT NULL)
GROUP BY impressionable_id, impressionable_type
I'd like to select from the table all last impressions that are unique on impressionable_id and impresssionable_type ordering by descending id and get the last 10
To explain this further
id, impressionabale_type, impressionable_id, action_name
50012, assignment, 2, show
50011, assignment, 1, show
50010, person, 1, show
50009, assignment, 1, show
50008, person, 5, show
50007, person, 4, show
50006, person, 3, show
50005, person, 1, show
50004, person, 1, show
50003, person, 2, show
50002, person, 2, show
50001, person, 1, show
50000, person, 1, show
Ideally I want this
50012, assignment, 2, show
50011, assignment, 1, show
50010, person, 1, show
50008, person, 5, show
50007, person, 4, show
50006, person, 3, show
50003, person, 2, show
I have tried distinct and group by but my sql knowledge is fair at best.
I get
PG::GroupingError: ERROR: column "impressions.id" must appear in the GROUP BY clause or be used in an aggregate function
Can someone shed some light please

Maybe thi will suit your needs:
SELECT t2.*
FROM (
SELECT DISTINCT impressionable_id, impressionabale_type
FROM impressions
WHERE impressions.action_name = 'show'
) t1, LATERAL (
SELECT *
FROM impressions
WHERE (t1.impressionable_id,t1.impressionabale_type) = (impressionable_id,impressionabale_type)
ORDER BY id DESC
LIMIT 1
) t2
ORDER BY id DESC
LIMIT 10
This will find all unique combinations of impressionable_id and impressionable_type and for each of them will find the row with the largest id in a LATERAL subquery.

select *
from (
select *,
row_number() over (
partition by impressionable_id, impressionable_type
order by id desc
) as rn
from impressions
where
user_id = 2
and action_name = 'show'
and message is not null
) s
where rn = 1

Related

Given an id, can you get the "index" of that items id in a sorted query

EDIT:
I am using android versions that don't have a sqlite version > 2.35. I cannot use ROW_NUMBER.
Given the following table:
id, date(long)
1, 100
2, 25
3, 5
4, 50
If I query for items sorted:
select * from items order by date:
id, date
3, 5
2, 25
4, 50
1, 100
If I have id 4, can I query to get the index in the sorted list, in this case index "3"
With ROW_NUMBER() window function:
SELECT rn
FROM (
SELECT *, ROW_NUMBER() OVER (ORDER BY date DESC) rn
FROM items
)
WHERE id = 4;
An alternative, for versions of SQLite prior to 3.25.0+ which don't support window functions:
SELECT COUNT(*) + 1 rn
FROM items
WHERE date > (SELECT date FROM items WHERE id = 4);
See the demo.
You can use ROW_NUMBER() to get the location of the row according to a custom ordering. The query can look like:
select rn
from (
select t.*, row_number() over(order by date) as rn from t
) x
where id = 4

Difference in the output of query, using rank() and CTE

My first query looks like:
select trans.* from
( select
acc_num,
acc_type,
trans_amount,
load_date,
rank() over(partition by acc_num order by load_date) as rk
from monetary
where rat_code = 123
) trans
where trans.rk =1;
second query looks like
with a as (
select *,
row_number() over(partition by acc_num order by load_date) as rn
from monetary
where rat_code = 123 )
select
acc_num,
acc_type,
trans_amount,
load_date
from a
where rn =1;
Can any one please help me I am getting different number of records for both the cases.
though the query is same.
Its because there is difference between rank and row_number.
Below example will show
Accno, dt, rank_col, rownum_col
100, 2-jun-2022, 1, 1
100, 3-jun-2022, 1, 2
100, 1-jul-2022, 1, 3
54, 2-jun-2022, 4, 1
54, 1-jul-2022, 4, 2
In above example, you can see row number will calculate unique row id. Whereas rank gives unique id but in a continuous manner. You can see from above example, rank=1 gives you 3 rows but rownum=1 gives only two.

BigQuery SQL: Sum of first N related items

I would like to know the sum of a value in the first n items in a related table. For example, I want to get the sum of a companies first 6 invoices (the invoices can be sorted by ID asc)
Current SQL:
SELECT invoices.company_id, SUM(invoices.amount)
FROM invoices
JOIN companies on invoices.company_id = companies.id
GROUP BY invoices.company_id
This seems simple but I can't wrap my head around it.
Consider also below approach
select company_id, (
select sum(amount)
from t.amounts amount
) as top_six_invoices_amount
from (
select invoices.company_id,
array_agg(invoices.amount order by invoices.invoice_id limit 6) amounts
from your_table invoices
group by invoices.company_id
) t
You can create order row numbers to the lines in a partition based on invoice id and filter to it, something like this:
with array_table as (
select 'a' field, * from unnest([3, 2, 1 ,4, 6, 3]) id
union all
select 'b' field, * from unnest([1, 2, 1, 7]) id
)
select field, sum(id) from (
select field, id, row_number() over (partition by a.field order by id desc) rownum
from array_table a
)
where rownum < 3
group by field
More examples for analytical examples here:
https://medium.com/#aliz_ai/analytic-functions-in-google-bigquery-part-1-basics-745d97958fe2
https://cloud.google.com/bigquery/docs/reference/standard-sql/analytic-function-concepts

Nth result in BigQuery Group By

I have a derived table like:
id, desc, total, account
1, one, 10, a
1, one, 9, b
1, one, 3, c
2, two, 27, c
I can do a simple
select id, desc, sum(total) as total from mytable group by id
but I want to add the equivalent first(account), first(total), second(account), second(total) to the output so it'd be:
id, desc, total, first_account, first_account_total, second_account, second_account_total
1, one, 21, a, 10, b, 9
2, two, 27, c, 27, null, 0
Any pointers?
Thanks in advance!
Below is for BigQuery Standard SQL
#standardSQL
SELECT id, `desc`, total,
arr[OFFSET(0)].account AS first_account,
arr[OFFSET(0)].total AS first_account_total,
arr[SAFE_OFFSET(1)].account AS second_account,
arr[SAFE_OFFSET(1)].total AS second_account_total
FROM (
SELECT id, `desc`, SUM(total) total,
ARRAY_AGG(STRUCT(account, total) ORDER BY total DESC LIMIT 2) arr
FROM `project.dataset.table`
GROUP BY id, `desc`
)
In cases when more than 2 first bins are required I would use below pattern that eliminates repeating of heavy repeated lines like arr[SAFE_OFFSET(1)].total AS second_account_total
#standardSQL
SELECT * FROM (SELECT NULL id, '' `desc`, NULL total, '' first_account, NULL first_account_total, '' second_account, NULL second_account_total) WHERE FALSE
UNION ALL
SELECT id, `desc`, total, arr[OFFSET(0)].*, arr[SAFE_OFFSET(1)].*
FROM (
SELECT id, `desc`, SUM(total) total,
ARRAY_AGG(STRUCT(account, total) ORDER BY total DESC LIMIT 2) arr
FROM `project.dataset.table`
GROUP BY id, `desc`
)
In above, first line sets layout of output while returning no rows at all because of WHERE FALSE, so then I don't need to explicitly parse struct's elements and provide aliases

creating a pseudo linked list in sql

I have a table that has the following columns
table: route
columns: id, location, order_id
and it has values such as
id, location, order_id
1, London, 12
2, Amsterdam, 102
3, Berlin, 90
5, Paris, 19
Is it possible to do a sql select statement in postgres that will return each row along with the id with the next highest order_id? So I want something like...
id, location, order_id, next_id
1, London, 12, 5
2, Amsterdam, 102, NULL
3, Berlin, 90, 2
5, Paris, 19, 3
Thanks
select
id,
location,
order_id,
lag(id) over (order by order_id desc) as next_id
from your_table
Creating testbed first:
CREATE TABLE route (id int4, location varchar(20), order_id int4);
INSERT INTO route VALUES
(1,'London',12),(2,'Amsterdam',102),
(3,'Berlin',90),(5,'Paris',19);
The query:
WITH ranked AS (
SELECT id,location,order_id,rank() OVER (ORDER BY order_id)
FROM route)
SELECT b.id, b.location, b.order_id, n.id
FROM ranked b
LEFT JOIN ranked n ON b.rank+1=n.rank
ORDER BY b.id;
You can read more on the window functions in the documentation.
yes:
select * ,
(select top 1 id from routes_table where order_id > main.order_id order by 1 desc)
from routes_table main