Comparing Array fields for Nulls,Equal,Not equal - Postgres - postgresql

Table Name: customers
customer_id Profiles dept_code
------------------------------------------
3361 ,15,31,4, ,01,02,
3362 ,32, ,01,03,
3363 ,04,
3364 ,1,20,21, ,01,02,03,
Table Name :customers_backup
customer_id Profiles dept_code
--------------------------------------
3361 ,15,31,4, ,01,02,
3362 ,32,33,34, ,01,03,
3363 ,10, ,04,
3364 ,1,20,21, ,01,02,03,
I am trying to update the PROFILE of CUSTOMERS table and the conditions are given below,
1) If customer profile is NULL => update customers_backup profile
2) If customer profile equal to customers_backup profile => just keep customer
profile
3) If customers profile <> to customers_backup profile => Keep customer profile and append the profile from customers_backup that is not in the customers table.
I need the following output :
Table Name: customers
customer_id Profiles dept_code
------------------------------------------------
3361 ,15,31,4, ,01,02,
3362 ,32,33,34, ,01,03, ( How to apply this condition?)
3363 ,10, ,04,
3364 ,1,20,21, ,01,02,03,
Below is what I wrote for condition 1 & 2. But they don't give the expected result.
update customers set profiles=
CASE WHEN (select unnest(array[customers.profiles])) is null
THEN customers_backup.profiles
WHEN (select unnest(array[customers.profiles])) =
(select unnest(array[customers_backup.profiles]))
THEN customers.profiles
WHEN (select unnest(array[customers.profiles])) <>
(select unnest(array[customers_backup.profiles])) --- Need help here
THEN user_enrollment_backup1.profiles
END FROM customers_backup
WHERE customers_backup.customer_id=customers.customer_id
AND customers_backup.dept_code= customers.dept_code;
Can someone help? Thanks in advance.

If you want to use actual arrays, you need to clean up your data first so it's in the correct format -- i.e. no leading/trailing commas. Once you have this, you can CAST your profiles field as an array data type and use array concatenation, similar to this:
SELECT
ARRAY(SELECT DISTINCT UNNEST(customers.profiles || customers_backup.profiles))
FROM ...
This should combine your array elements into one array, then unnest & remove duplicate elements, and then finally combine back into the final array. I don't think they will be ordered, but this should get you started.
It looks like your profile values are not real arrays, so you'd need to CAST them as arrays in the query above.

You may use an UPDATE table FROM syntax.
Note that arrays can be compared using = and <> operators.
Only for finding the union (3rd case ), you could use UNNEST.
UPDATE customers AS c
SET profiles = CASE WHEN c.profiles IS NULL
THEN cb.profiles
WHEN array[c.profiles] = array[cb.profiles]
THEN c.profiles
WHEN array[c.profiles] <> array[cb.profiles]
THEN ( select string_agg(a,'')
from (
select distinct
unnest(array[RTRIM(c.profiles,',')::VARCHAR] || array[cb.profiles]) as a
) s
)
END
FROM customers_backup as cb
WHERE c.customer_id = cb.customer_id;
Also note that If you create extension intarray, you may use simple | operator for array UNIONs.
DEMO

Related

How to work with data values formatted [{}, {}, {}]

I apologize if this is a simple question - I had some trouble even formatting the question when I was trying to Google for help!
In one of the tables I am working with, there's data value that looks like below:
Invoice ID
Status
Product List
1234
Processed
[{"product_id":463153},{"product_id":463165},{"product_id":463177},{"pid":463218}]
I want to count how many products each order has purchased. What is the proper syntax and way to count the values under "Product List" column? I'm aware that count() is wrong, and I need to maybe extract the data from the string value.
select invoice_id, count(Product_list)
from quote_table
where status = 'processed'
group by invoice_id
You can use a JSON function named: json_array_length and cast this column like a JSON data type (as long as possible), for example:
select invoice_id, json_array_length(Product_list::json) as count
from quote_table
where status = 'processed'
group by invoice_id;
invoice_id | count
------------+-------
1234 | 4
(1 row)
If you need to count a specific property of the json column, you can use the query below.
This query solves the problem by using create type, json_populate_recordset and subquery to count product_id inside json data.
drop type if exists count_product;
create type count_product as (product_id int);
select
t.invoice_id,
t.status,
(
select count(*) from json_populate_recordset(
null::count_product,
t.Product_list
)
where product_id is not null
) as count_produto_id
from (
-- symbolic data to use in query
select
1234 as invoice_id,
'processed' as status,
'[{"product_id":463153},{"product_id":463165},{"product_id":463177},{"pid":463218}]'::json as Product_list
) as t

How to get ids of grouped by rows in postgresql and use result?

I have a table containing transactions with an amount. I want to create a batch of transactions so that the sum of amount of each 'group by' is negative.
My problematic is to get all ids of the rows concerned by a 'group by' where each group is validate by a sum condition.
I find many solutions which don't work for me.
The best solution I found is to request the db a first time with the 'group by' and the sum, then return ids to finally request the db another time with all of them.
Here an example of what I would like (it doesn't work!) :
SELECT * FROM transaction_table transaction
AND transaction.id IN (
select string_agg(grouped::character varying, ',' ) from (
SELECT array_agg(transaction2.id) as grouped FROM transaction_table transaction2
WHERE transaction2.c_scte='c'
AND (same conditions)
GROUP BY
transaction2.motto ,
transaction2.accountBnf ,
transaction2.payment ,
transaction2.accountClt
HAVING sum(transaction2.amount)<0
)
);
the result of the array_agg is like:
{39758,39759}
{39757,39756,39755,39743,39727,39713}
and the string_agg is :
{39758,39759},{39757,39756,39755,39743,39727,39713}
Now I just need to use them but I don't know how to...
unfortunatly, it doesn't work because of type casting :
ERROR: operator does not exist: integer = integer[]
IndiceĀ : No operator matches the given name and argument type(s). You might need to add explicit type casts.
Maybe you are looking for
SELECT id, motto, accountbnf, payment, accountclnt, amount
FROM (SELECT id, motto, accountbnf, payment, accountclnt, amount,
sum(amount)
OVER (PARTITION BY motto, accountbnf, payment, accountclnt)
AS group_total
FROM transaction_table) AS q
WHERE group_total < 0;
The inner SELECT adds an additional column using a window function that calculates the sum for each group, and the outer query removes all results where that sum is not negative.
Finally I found this option using the 'unnest' method. It works perfectly.
Array_agg bring together all ids in different array
unnest flattened all of them
This comes from here
SELECT * FROM transaction_table transaction
WHERE transaction.id = ANY(
SELECT unnest(array_agg(transaction2.id)) as grouped FROM transaction_table transaction2
WHERE transaction2.c_scte='c'
AND (same conditions)
GROUP BY
transaction2.motto ,
transaction2.accountBnf ,
transaction2.payment ,
transaction2.accountClt
HAVING sum(transaction2.amount)<0
);
The problem with this solution is that hibernate doesn't take into account the array_agg method.

oracle: grouping on merged columns

I have a 2 tables FIRST
id,rl_no,adm_date,fees
1,123456,14-11-10,100
2,987654,10-11-12,30
3,4343,14-11-17,20
and SECOND
id,rollno,fare,type
1,123456,20,bs
5,634452,1000,bs
3,123456,900,bs
4,123456,700,bs
My requirement is twofold,
1, i first need to get all columns from both tables with common rl_no. So i used:
SELECT a.ID,a.rl_no,a.adm_date,a.fees,b.rollno,b.fare,b.type FROM FIRST a
INNER JOIN
SECOND b ON a.rl_no = b.rollno
The output is like this:
id,rl_no,adm_date,fees,rollno,fare,type
1,123456,14-11-10,100,123456,20,bs
1,123456,10-11-12,100,123456,900,bs
1,123456,14-11-17,100,123456,700,bs
2,Next i wanted to get the sum(fare) of those rollno that were common between the 2 tables and also whose fare >= fees from FIRST table group by rollno and id.
My query is:
SELECT x.ID,x.rl_no,,x.adm_date,x.fees,x.rollno,x.type,sum(x.fare) as "fare" from (SELECT a.ID,a.rl_no,a.adm_date,a.fees,b.rollno,b.fare,b.type FROM FIRST a
INNER JOIN
SECOND b ON a.rl_no = b.rollno) x, FIRST y
WHERE x.rollno = y.rl_no AND x.fare >= y.fees AND x.type IS NOT NULL GROUP BY x.rollno,x.ID ;
But this is throwing in exceptions.
ORA-00979: not a GROUP BY expression
00979. 00000 - "not a GROUP BY expression"
The expected output will be like this:
id,rollno,adm_date,fare,type
1,123456,14-11-10,1620,bs
So could someone care to show an oracle newbie what i'm doing wrong here?
It looks like there's a couple different problems here;
Firstly, you're trying to group by an x.ID column which doesn't exist; it looks like you'll want to add ID to the selected columns in your sub-query.
Secondly, when aggregating with GROUP BY, all selected columns need to be either listed in the GROUP BY statement or aggregated. If you're grouping by rollno and ID, what do you want to have happen to all the extra values for adm_date, fees, and type? Are those always going to be the same for each distinct rollno and ID pair?
If so, simply add them to the GROUP BY statement, ie,
GROUP BY adm_date, fees, type, rollno, ID
If not, you'll need to work out exactly how you want to select which one to be output; If you've got output like your example (adding in an ID column here)
ID,adm_date,fees,rollno,fare,type
1,14-11-10,100,123456,20,bs
1,10-11-12,100,123456,900,bs
1,14-11-17,100,123456,700,bs
Call that result set 'a'. If I run;
SELECT a.ID, a.rollno, SUM(a.fare) as total_fare
FROM a
GROUP BY a.ID, a.rollno
Then the result will be a single row;
ID,rollno,total_fare
1,123456,1620
So, if you also select the adm_date, fees, and type columns, oracle has no idea what you mean to do with them. You're not using them for grouping, and you're not telling oracle how you want to pick which one to use.
You could do something like
SELECT a.ID,
FIRST(a.adm_date) as first_adm_date,
FIRST(a.fees) as first_fees,
a.rollno,
SUM(a.fare) as total_fare,
FIRST(a.type) as first_type
FROM a
GROUP BY a.ID, a.rollno
Which would give the result;
ID,first_adm_date,first_fees,rollno,total_fare,first_type
1,14-11-10,100,123456,1620,bs
I'm not sure if that's what you mean to do though.

Specifice order to tables in postgres

I just created a temporary table as:
create temporary table userAndProductSales as
select p.p_name, u.u_name, u.s_price, u.quantity
from product p
join userAndStates u
on p.s_id = u.s_id
Now I want to select some columns with a particular order. For example, I want the select to give me an output of:
u_name1 p_name1
u_name1 p_name2
u_name1 p_name3
u_name1 p_name4
...
u_name2 p_name1
u_name2 p_name2
u_name2 p_name3
....
and so on and so forth. How do I get this ouput? I've tried something on the lines of:
select (select u_name from userandproductsales order by u_name), p_name from userandproductsales
but I'm getting an error
UPDATE: Figured out that the table I'm joining isn't giving me the correct data I want. Thanks for the help though.
Here is how to use ORDER BY :
SELECT * from userandstatesales
order by u_name , p_name
Unless there is a reason for creating a temporary table (like needing to access it later in the same session), you should avoid the expense and simply do a order by from your select. For example:
select p.p_name, u.u_name, u.s_price, u.quantity
from product p
join userAndStates u
on p.s_id = u.s_id
order by u.u_name, p.p_name;

Postgresql query involving three tables

I have three tables, say:
(1) Name: Groups
Columns: gid, groupname
(2) Name: Profiles
Columns: profileid, profname
(3) Name: GroupsProfiles
Columns: gid, profileid - corresponds to the columns in the previous tables
Now, say I have an array of profileid's, I would like to construct a query which finds the corresponding gid in GroupsProfiles and then the corresponding groupname in Groups from that.
I have been playing about with joins and things but not quite getting what I want. Any ideas?
Use a join (replace your_profile_id with the profile ID you care about):
SELECT p.profileid
, g.gid
, g.GroupName
FROM Groups g
, Profiles p
, GroupsProfiles gp
WHERE g.gid = gp.gid
AND p.profileid = gp.profileid
AND p.profileid = your_profile_id
You would run this query for each profile ID. If you want to get the information for all profiles, or for a subset of profiles, you could remove the condition on your_profile_id, or you could also use an IN clause if you know which ones you want, e.g.
SELECT g.gid
, g.GroupName
FROM Groups g
, Profiles p
, GroupsProfiles gp
WHERE g.gid = gp.gid
AND p.profileid = gp.profileid
AND p.profileid IN (profile_id_1, profile_id_2, ... ,profile_id_N)