how to SET value to 0 IF dta is not found in query POSTGRESQL - postgresql

i have table like this
id | name | planned_amount | actual | avail
1 ABC 100 123 100
2 DEF 200 345 200
3 uytuyn 9000 311 300
4 oiui 890 200 200
above data output from query like this:
select a.name,
e.planned_amount,
dd.sum as actual,
e.planned_amount - dd.sum as avail
from crossovered_budget_lines as e,
account_analytic_account as a,
(select analytic_account_id, sum(debit+credit)
from account_move_line
where date between '2021-01-01' and '2021-05-01'
group by analytic_account_id) as dd
where e.analytic_account_id = a.id
and dd.analytic_account_id = .id
limit 5
when range data from table account_move_line is null ( no output data ), i want to output like this, column actual give 0 value :
id | name | planned_amount | actual | avail
1 ABC 100 0 100
2 DEF 200 0 200
3 uytuyn 9000 0 300
4 oiui 890 0 200
how to query to produce the output above ? iam stuck , thanks

Related

indicator for increase over time

I'm trying to create an indicator for value increase over time within a group. In particular, I'm trying to flag certain grp if value ever increases by 50% over time.
I have a raw data that looks like:
id grp value_dt value
--------------------------------
1 1 11/20/20 1.4
1 1 11/21/20 0.8
1 1 11/24/20 2.8
1 1 11/25/20 2.5
1 2 11/29/20 1.5
1 2 12/1/20 1.6
2 1 11/21/20 0.8
2 2 11/26/20 0.9
2 3 12/1/20 0.9
2 3 12/3/20 2.8
You can see that for id = 1 and grp = 1, the value fluctuates as it increases and decreases over time, but because it had increase over time between 11/21/20 and 11/24/20 from 0.8 to 2.8 (greater than 50% increase), I want to flag the whole grp 1. I want my output to look like:
id grp val_ind
-----------------------
1 1 1
1 2 0
2 1 0
2 2 0
2 3 1
I can only think of using min and max (something like below), which doesn't include the 'over the time' factor in...
select id,
grp,
min(value) as min_grp,
max(value) as max_grp,
(max_grp - min_grp) as val_diff,
case when val_diff >= min_grp * 1.5 then 1 else 0 end as val_ind
If anyone can offer their advice, I will greatly appreciate it!
I think you want to know if at any point at time there is an increase of 50% , you flag that group. if yes , here is how you can do it,
you need to use cte and window functions :
; WITH cte AS (
SELECT *
, CASE WHEN COALESCE(LEAD(value) OVER (PARTITION BY id, grp ORDER BY value_dt),0) >= value* 2 THEN 1 ELSE 0 END val_ind
FROM ttt
)
SELECT
id , grp , MAX(val_ind) val_ind
FROM cte
GROUP BY
id , grp
id | grp | val_ind
-: | --: | ------:
1 | 1 | 1
1 | 2 | 0
2 | 1 | 0
2 | 2 | 0
2 | 3 | 1
db<>fiddle here

Create Pivot Table using PostgreSQL

I have a table like this:
type code desc store Sales/Day Stock
-----------------------------------------------
1 AA1 abc 101 3 6
1 AA2 abd 101 4 0
1 AA3 abf 101 4 3
2 BA1 bba 101 5 1
2 BA2 bbc 101 2 1
1 AA1 abc 102 1 4
1 AA2 abd 102 2 0
2 BA1 bba 102 4 2
2 BA2 bbc 102 5 5
etc.
How I can show the result table like this:
type code desc Store 101 Store 102
Sales/Day | Stock Sales/Day | Stock
--------------------------------------------------------------
1 AA1 abc 3 6 1 4
1 AA2 abd 4 0 2 0
1 AA3 abf 4 3 0 0
2 BA1 bba 5 1 4 2
2 BA2 bbc 2 1 5 5
etc.
Note:
Colspan is only display.
demo:db<>fiddle
First way: FILTER
SELECT
type,
code,
"desc",
COALESCE(SUM(sales_day) FILTER (WHERE store = 101)) as sales_day_101,
COALESCE(SUM(stock) FILTER (WHERE store = 101), 0) as stock_101,
COALESCE(SUM(sales_day) FILTER (WHERE store = 102), 0) as sales_day_102,
COALESCE(SUM(stock) FILTER (WHERE store = 102), 0) as stock_102
FROM mytable
GROUP BY type, code, "desc"
ORDER BY type, code
Aggregating your values. I took SUM but in your case with distinct rows many other aggregate functions would do it. FILTER allows you to aggregate only one store.
The COALESCE is to avoid NULL values if no values are present for one aggregation (like AA3 in store 102).
Second way, CASE WHEN
SELECT
type,
code,
"desc",
SUM(CASE WHEN store = 101 THEN sales_day ELSE 0 END) as sales_day_101,
SUM(CASE WHEN store = 101 THEN stock ELSE 0 END) as stock_101,
SUM(CASE WHEN store = 102 THEN sales_day ELSE 0 END) as sales_day_102,
SUM(CASE WHEN store = 102 THEN stock ELSE 0 END) as stock_102
FROM mytable
GROUP BY type, code, "desc"
ORDER BY type, code
The idea is the same, but the newer FILTER function is replace by the more common CASE clause.
Notice that "desc" is a reserved word in Postgres. So I strictly recommend to rename your column.

Redshift - Get a value from one column A for each ID in the grouping ID column B based on max value in another column C

I have a sql problem (on Redshift) where I need to get the value from column index for each id in column id based on max value in column final_score and put this value in a new column fav_index. score2 equals to the value of score1 where index n = index n + 1, for example, for id = abc1, index = 0 and score1 = 10 the value of score2 will be the value of score1 where index = 1 and the value of final_score is the difference between score1 and score2.
It's easier if you look at below table score. This table score is a result of a sql query which is shown later below.
id index score1 score2 final_score
abc1 0 10 20 10
abc1 1 20 45 25
abc1 2 45 (null) (null)
abc2 0 5 10 5
abc2 1 10 (null) (null)
abc3 0 50 30 -20
abc3 1 30 (null) (null)
So, the resulting table containing column fav_index should look like this:
id index score1 score2 final_score fav_index
abc1 0 10 20 10 0
abc1 1 20 45 25 1
abc1 2 45 (null) (null) 0
abc2 0 5 10 5 0
abc2 1 10 (null) (null) 0
abc3 0 50 30 -20 0
abc3 1 30 (null) (null) 0
Below is the script to generate table score from table story:
select
m.id,
m.index,
max(m.max) as score1,
fmt.score2,
round(fmt.score2 - max(m.max), 1) as final_score
from
(select
sv.id,
case when sv.story_number % 2 = 0 then cast(sv.story_number / 2 - 1 as int) else cast(floor(sv.story_number/2) as int) end as index,
max(sv.score1)
from
story as sv
group by
sv.id,
index,
sv.score1
order by
sv.id,
index
) as m
left join
(select
sv.id,
case when sv.story_number % 2 = 0 then cast(sv.story_number / 2 - 1 as int) else cast(floor(sv.story_number/2) as int) end as index,
max(score1) as score2
from
story as sv
group by
id,
index
) as fmt
on
m.id = fmt.id
and
m.index = fmt.index - 1
group by
m.id,
m.index,
fmt.score2
Table story is as below:
id story_number score1
abc1 1 10
abc1 2 10
abc1 3 20
abc1 4 20
abc1 5 45
abc1 6 45
The only solution I can think of is to do something like,
select id, max(final_score) from score group by id
and then join it back to the long script above (which was used to generate table score). I really want to avoid writing such a long script to get just 1 extra column of information that I need.
Is there a better way to do this?
Thank you!
Update: answer in mysql is also accepted. thanks!
After spending more hours on this and asking people around, I finally figured out a solution by referring to this window function documentation - PostgreSQL https://www.postgresql.org/docs/9.1/static/tutorial-window.html
I basically added 2 x select statements at the top and 1 x where statement at the very bottom. The where statement is to take care of the rows where final_score = null because otherwise the rank() function will rank them as 1.
My code then becomes:
select
id, index, final_score, rank, case when rank = 1 then index else null end as fav_index
from
(select
id, index, final_score, rank() over (partition by id order by final_score desc)
from
(select
m.id,
m.index,
max(m.max) as score1,
fmt.score2,
round(fmt.score2 - max(m.max), 1) as final_score
from
(select
sv.id,
case when sv.story_number % 2 = 0 then cast(sv.story_number / 2 - 1 as int) else cast(floor(sv.story_number/2) as int) end as index,
max(sv.score1)
from
story as sv
group by
sv.id,
index,
sv.score1
order by
sv.id,
index
) as m
left join
(select
sv.id,
case when sv.story_number % 2 = 0 then cast(sv.story_number / 2 - 1 as int) else cast(floor(sv.story_number/2) as int) end as index,
max(score1) as score2
from
story as sv
group by
id,
index
) as fmt
on
m.id = fmt.id
and
m.index = fmt.index - 1
group by
m.id,
m.index,
fmt.score2)
where
final_score is not null)
And the result is as follows:
id index final_score rank fav_index
abc1 0 10 2 (null)
abc1 1 25 1 1
abc2 0 5 1 0
abc3 0 -20 1 0
Result is slightly different than what I stated in the question, however, the fav_index for each id is identified and this is what I needed really. Hope this might help someone. Cheers

Calculate the percentage of column data in postgres

I have table column like;
Documents
------------
1 2 3
1 2
2 3
1
4 5 1 3
Data under document column indicates the type of document; for e.g
1 indicates passport
2 indicates School id and so on.....
I want the column data as separate data with percentage calculation. like;
Basically i want to show percentage for each data...
Documents percentage
-------------------------
1 10%
2 2%
3 1%
4 25%
5 30%
I want to show data as separate data with their percentage.
can we build query in postgres to achive this????
You should convert the strings to arrays and unnest them, then you can calculate total and percentages:
create table test (documents text);
insert into test values
('1 2 3'),
('1 2'),
('2 3'),
('1'),
('4 5 1 3');
with docs as (
select doc
from test, unnest(string_to_array(documents, ' ')) doc
),
total as (
select count(*) as total
from docs
)
select doc, count(doc), count(doc)* 100/ total as percentage
from docs, total
group by doc, total
order by 1;
doc | count | percentage
-----+-------+------------
1 | 4 | 33
2 | 3 | 25
3 | 3 | 25
4 | 1 | 8
5 | 1 | 8
(5 rows)
with t(s) as ( values
('1 2 3'),('1 2'),('2 3'),('1'),('4 5 1 3')
)
select distinct s,
count(*) over(partition by s) * 100.0 /
count(*) over() as percentage
from (
select regexp_split_to_table(s, ' ') as s
from t
) t
;
s | percentage
---+---------------------
5 | 8.3333333333333333
4 | 8.3333333333333333
3 | 25.0000000000000000
1 | 33.3333333333333333
2 | 25.0000000000000000

I need to combine 2 queries postgis

I have 2 queries:
1st query :
SELECT ST_AsText( ST_MakeLine(sp) )
FROM
(SELECT
ST_PointN(geom, generate_series(1, ST_NPoints(geom))) as sp
FROM
-- extract the individual linestrings
(SELECT (ST_Dump(ST_Boundary(geom))).geom
FROM geometriess
) AS linestrings
) AS segments;
In the table there is : "POLYGON((0 0,1 0,1 1,0 1,0 0))"
After this query there will be : "LINESTRING(0 0,1 0,1 1,0 1,0 0)"
And 2nd query :
with line as (select geom from geometries)
select ST_X(ST_PointN(geom,num)) as x,
ST_Y(ST_PointN(geom,num)) as y
from line,
(select generate_series(1, (select ST_NumPoints(geom) from line)) as num)
as series;
it splits linestring to points x and y.
I need to combine them, but I don't know how.
The following test table creates two polygons, with different numbers of points, combines them together, and then grabs each point of the boundary using generate_series with st_npoints to iterate through each polygon.
create table test (id serial, geom geometry);
insert into test (geom) values (ST_GeomFromText('POLYGON((0 0, 0 1, 1 1, 1 0, 0 0))'));
insert into test (geom) values (ST_GeomFromText('POLYGON((100 100, 100 200, 200 200, 250 300, 200 100, 100 100))'));
with dumped as (select (st_dump(geom)) as dump from
(select st_collect(geom) as geom from test) g),
points as (select (dump).path, st_npoints((dump).geom) as npoints, (dump).geom from dumped),
polygons as (select path[1] as polygonid,
st_boundary(geom) as geom,
generate_series(1,npoints) as x from points)
select polygonid,
st_x(st_pointn(geom,x)),
st_y(st_pointn(geom,x))
from polygons;
returns:
polygonid | st_x | st_y
-----------+------+------
1 | 0 | 0
1 | 0 | 1
1 | 1 | 1
1 | 1 | 0
1 | 0 | 0
2 | 100 | 100
2 | 100 | 200
2 | 200 | 200
2 | 250 | 300
2 | 200 | 100
2 | 100 | 100