Recursive sum for each row - postgresql

Consider this testing data:
CREATE TABLE IF NOT EXISTS area (
id integer,
parent_id integer,
name text,
population integer
);
INSERT INTO area VALUES
(1, NULL, 'North America', 0),
(2, 1, 'United States', 0),
(3, 1, 'Canada', 39),
(4, 1, 'Mexico', 129),
(5, 2, 'Contiguous States', 331),
(6, 2, 'Non-contiguous States', 2);
id
parent_id
name
population
1
NULL
North America
0
2
1
United States
0
3
1
Canada
39
4
1
Mexico
129
5
2
Contiguous States
331
6
2
Non-contiguous States
2
Note that population (in millions) means here the additional population, excluding area's children.
How do I query the recursive sum for each row? I need to get something like this:
name
sum
North America
501
United States
333
Canada
39
Mexico
129
Contiguous States
331
Non-contiguous States
2

Here is how I solved it:
WITH RECURSIVE parent AS (
SELECT
*,
id AS root -- Emit the root ID
FROM area
-- No WHERE clause, since we treat every row as a possible root
UNION ALL
SELECT
area.*,
parent.root -- Pass the root ID to the children
FROM area
JOIN parent ON parent.id = area.parent_id
)
SELECT
name,
sum
FROM (
SELECT
root,
SUM(population)
FROM parent
GROUP BY root -- Here is where we need the root IDs
) cumulative
JOIN area ON area.id = cumulative.root; -- Use the nice names instead of IDs

Related

tsql max and group by not working properly

I got the following table articles:
ID
category
price
1
category1
10
2
category1
55
3
category2
15
4
category3
20
5
category4
25
I would like to get the highest price of each category.
The result would be:
ID
category
price
2
category1
55
3
category2
15
4
category3
20
5
category4
25
select Max(price), ID, category from article
group by ID,category
returns:
ID
category
price
1
category1
10
2
category1
55
3
category2
15
4
category3
20
5
category4
25
Unfortunately I get both rows for category 1. But I only would like to have the highest price in category 1 which is 55.
Can someone help me?
see above
Try this...
I've reproduced your sample data and then added a rnk column which ranks by price descending witin each category, used this in the subquery and just returned anything where rank is 1.
DECLARE #articles TABLE (ID int, Category varchar(20), Price float)
INSERT INTO #articles VALUES
(1, 'category1', 10),
(2, 'category1', 55),
(3, 'category2', 15),
(4, 'category3', 20),
(5, 'category4', 25)
SELECT
ID, Category, Price
FROM (
SELECT
ID, Category, Price
, RANK() OVER(PARTITION BY Category ORDER BY Price DESC) as rnk
FROM #articles
) a
WHERE a.rnk = 1
Which gives these results
Bote If you have two articles for the same category with the same price, both will be returned.
--===== This is NOT a part of the solution.
-- We're just making "Readily Consumable Test Data" here.
-- This is how you should post sample data to help those
-- that would help you. You'll get more thumbs up on your
-- questions, as well
SELECT *
INTO #Articles
FROM (VALUES
(1, 'category1', 10)
,(2, 'category1', 55)
,(3, 'category2', 15)
,(4, 'category3', 20)
,(5, 'category4', 25)
)d(ID,category,price)
;
--===== One possible easy solution that will also display "ties".
WITH cteRankByCategory AS
(
SELECT *,DR = DENSE_RANK() OVER (PARTITION BY Category ORDER BY Category, Price DESC)
FROM #Articles
)
SELECT ID,Category,MaxPrice = Price
FROM cteRankByCategory
WHERE DR = 1
ORDER BY Category
;

lag function over multiple rows in Postgresql

I have a table like this
b.b_id fp.fp_id b.name
1 10 Dan
1
1
1
2 15 Michelle
2
3 20 Steve
3
3
Im trying to get this output
b.b_id fp.fp_id b.name
1 10 Dan
1 Dan
1 Dan
1 Dan
2 15 Michelle
2 Michelle
3 20 Steve
3 Steve
3 Steve
My idea was using the lag function but with this code i am only able to fill 1 row below.
select b.b_id,fp.fp_id,
case
when fp.fp_id is null then lag(b.name,1) over (partition by b.b_id order by b.b_id,fp.fp_id)
else b.name
end as name
from b
left join fp on fp.id = b.fp_id
Output at the moment
b.b_id fp.fp_id b.name
1 10 Dan
1 Dan
1
1
2 15 Michelle
2 Michelle
3 20 Steve
3 Steve
3
Is there some easy way to solve this?
CREATE temp TABLE test101 (
id bigint,
fp_id bigint,
name text
);
INSERT INTO test101
VALUES (1, 10, 'dan'),
(1, NULL, NULL),
(1, NULL, NULL),
(1, NULL, NULL),
(2, 15, 'Michelle'),
(2, NULL, NULL),
(3, 20, 'Steve'),
(3, NULL, NULL),
(3, NULL, NULL);
SELECT
id,
fp_id,
first_value(name) OVER (PARTITION BY id ORDER BY id ASC nulls LAST, fp_id NULLS LAST)
FROM
test101;

Get last row from group, limit number of results in PostgreSQL

I have a table with records representing a log, I omit rest of the columns in this example.
The id-column is autoincrement, item_id represents an item in app.
I need to get the latest item_id, for example two or three
CREATE TABLE "log" (
"id" INT,
"item_id" INT
);
-- TRUNCATE TABLE "log";
INSERT INTO "log" ("id", "item_id") VALUES
(1, 1),
(2, 2),
(3, 1),
(4, 1),
(5, 3),
(6, 3);
Basic query will list all results, latest at the top:
SELECT *
FROM "log"
ORDER BY "id" DESC
id item_id
6 3
5 3
4 1
3 1
2 2
1 1
I would like to have just two (LIMIT 2) last item_ids with their id. Last means - inserted last (ORDER BY id).
id item_id
6 3
4 1
Last three would be
id item_id
6 3
4 1
2 2
Once an item_id is returned, it is not returned again. So LIMIT 4 would return only three rows because there are only three unique item_id.
I am probably missing something. I already tried various combinations of DISTINCT OF, GROUP BY, LIMIT etc.
UPDATE #1:
After I tested query by S-man (below), I found out that it works for the data I provided howerer it does not work in general, for another set of data (sequence of item_id A, B and A again.). Here is another data set:
TRUNCATE TABLE "log";
INSERT INTO "log" ("id", "item_id") VALUES
(1, 1),
(2, 2),
(3, 3),
(4, 3),
(5, 1),
(6, 3);
Data in DB, ordered by id desc:
id item_id
6 3
5 1
4 3
3 3
2 2
1 1
Expected result for last three item_id
6 3
5 1
2 2
Well, after three changes, now we come back to the very first idea:
Just take DISTINCT ON:
demo:db<>fiddle
SELECT
*
FROM (
SELECT DISTINCT ON (item_id) -- 1
*
FROM log
ORDER BY item_id, id DESC
) s
ORDER BY id DESC -- 2
LIMIT 2
Returns exact one record of an ordered group. You group is the item_id, the order is id DESC, so you get the highest id for each item_id
Reorder by id DESC (instead of the previously ordered item_id) and limit your query output.

TSQL, Pivot rows into single columns

Before, I had to solve something similar:
Here was my pivot and flatten for another solution:
I want to do the same thing on the example below but it is slightly different because there are no ranks.
In my previous example, the table looked like this:
LocationID Code Rank
1 123 1
1 124 2
1 138 3
2 999 1
2 888 2
2 938 3
And I was able to use this function to properly get my rows in a single column.
-- Check if tables exist, delete if they do so that you can start fresh.
IF OBJECT_ID('tempdb.dbo.#tbl_Location_Taxonomy_Pivot_Table', 'U') IS NOT NULL
DROP TABLE #tbl_Location_Taxonomy_Pivot_Table;
IF OBJECT_ID('tbl_Location_Taxonomy_NPPES_Flattened', 'U') IS NOT NULL
DROP TABLE tbl_Location_Taxonomy_NPPES_Flattened;
-- Pivot the original table so that you have
SELECT *
INTO #tbl_Location_Taxonomy_Pivot_Table
FROM [MOAD].[dbo].[tbl_Location_Taxonomy_NPPES] tax
PIVOT (MAX(tax.tbl_lkp_Taxonomy_Seq)
FOR tax.Taxonomy_Rank in ([1],[2],[3],[4],[5],[6],[7],[8],[9],[10],[11],[12],[13],[14],[15])) AS pvt
-- ORDER BY Location_ID
-- Flatten the tables.
SELECT Location_ID
,max(piv.[1]) as Tax_Seq_1
,max(piv.[2]) as Tax_Seq_2
,max(piv.[3]) as Tax_Seq_3
,max(piv.[4]) as Tax_Seq_4
,max(piv.[5]) as Tax_Seq_5
,max(piv.[6]) as Tax_Seq_6
,max(piv.[7]) as Tax_Seq_7
,max(piv.[8]) as Tax_Seq_8
,max(piv.[9]) as Tax_Seq_9
,max(piv.[10]) as Tax_Seq_10
,max(piv.[11]) as Tax_Seq_11
,max(piv.[12]) as Tax_Seq_12
,max(piv.[13]) as Tax_Seq_13
,max(piv.[14]) as Tax_Seq_14
,max(piv.[15]) as Tax_Seq_15
-- JOIN HERE
INTO tbl_Location_Taxonomy_NPPES_Flattened
FROM #tbl_Location_Taxonomy_Pivot_Table piv
GROUP BY Location_ID
So, then here is the data I would like to work with in this example.
LocationID Foreign Key
2 2
2 670
2 2902
2 5389
3 3
3 722
3 2905
3 5561
So I have some data that is formatted like this:
I have used pivot on data like this before--But the difference was it had a rank also. Is there a way to get my foreign keys to show up in this format using a pivot?
locationID FK1 FK2 FK3 FK4
2 2 670 2902 5389
3 3 722 2905 5561
Another way I'm looking to solve this is like this:
Another way I could look at doing this is I have the values in:
this form as well:
LocationID Address_Seq
2 670, 5389, 2902, 2,
3 722, 5561, 2905, 3
etc
is there anyway I can get this to be the same?
ID Col1 Col2 Col3 Col4
2 670 5389, 2902, 2
This, adding a rank column and reversing the orders, should gives you what you require:
SELECT locationid, [4] col1, [3] col2, [2] col3, [1] col4
FROM
(
SELECT locationid, foreignkey,rank from #Pivot_Table ----- temp table with a rank column
) x
PIVOT (MAX(x.foreignkey)
FOR x.rank in ([4],[3],[2],[1]) ) pvt

Selecting specific row from a sub query depending on lowest priority

I have a table with Clients and their Insurance Providers. There is a column called Priority that ranges from 1-8. I want to be able to select the lowest priority insurance into my 'master table' I have a query that provides Fees, Dates, Doctors etc. and I need a subquery that I can join to the Main query on Client_ID The priority doesn't always start with 1. The Insurance Table is the Many side of the relationship
Row# Client_id Insurance_id Priority active?
1 333 A 1 Y
2 333 B 2 Y
3 333 C 1 N
4 222 D 6 Y
5 222 A 8 Y
6 444 C 4 Y
7 444 A 5 Y
8 444 B 6 Y
Answer should be
Client_id Insurance_id Priority
333 A 1
222 D 6
444 C 4
I was able to achieve the results I think you're asking for pretty easily utilizing SQL's ROW_NUMBER() function:
declare #tbl table
(
Id int identity,
ClientId int,
InsuranceId char(1),
[Priority] int,
Active bit
)
insert into #tbl (ClientId, InsuranceId, [Priority], Active)
values (1, 'A', 1, 1),
(1, 'A', 2, 1),
(1, 'B', 3, 1),
(1, 'B', 4, 1),
(1, 'C', 1, 1),
(1, 'C', 2, 0),
(2, 'C', 1, 1),
(2, 'C', 2, 1)
select Id, ClientId, InsuranceId, [Priority]
from
(
select Id,
ClientId,
InsuranceId,
[Priority],
ROW_NUMBER() OVER (PARTITION BY ClientId, InsuranceId ORDER BY [Priority] desc) as RowNum
from #tbl
where Active = 1
) x
where x.RowNum = 1
Results:
(8 row(s) affected)
Id ClientId InsuranceId Priority
----------- ----------- ----------- -----------
2 1 A 2
4 1 B 4
5 1 C 1
8 2 C 2
(4 row(s) affected)