Postgresql query involving three tables - postgresql

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)

Related

Postgresql query with double has_many relationships

I have this complex data relationship.
POSTGRESQL FIDDLE: https://www.db-fiddle.com/f/vm2z8qLuddzcHEgyaMnCbc/3
"Item Group" has many "items" through "item_ads" table.
So an Item Group has many part_number.
reports table contains the number of clicks for each day for each adgroupid.
Each adgroupid has_many part_numbers. (table: product_ads)
Now, I want to SUM all reports.clicks for each item_groups.id using the part_number to linked the tables.
After this, I have to consider only reports.adgroupid which are included in the part_numbers of item_group. So if "Item group" has three part_number (A, B, C) can be considered all adgroupid that contains A,B, or C but nothing more. If adgroupid contains part_number D it cannot be considered for clicks sum.
Expected results
I have to have a table with lots of item_group_ids.
I am looking for the PostgreSQL query to achieve this table.
First, let's build the query up in parts. It sounds like you already know how to get from item_group and adgroup to part_number, just not about how to join them. I've added a query that removes duplicates for part 1 of your question, but putting them into a CTE:
WITH unique_part_numbers AS (
SELECT DISTINCT item_groups.id AS item_group_id,
part_number
FROM item_groups
JOIN item_ads ON item_group_id = item_groups.id
JOIN items ON items.id = item_ads.item_id
)
SELECT unique_part_numbers.item_group_id, SUM(clicks)
FROM unique_part_numbers
JOIN product_ads ON product_ads.part_number = unique_part_numbers.part_number
JOIN reports ON product_ads.adgroupid = reports.adgroupid
GROUP BY item_group_id
About the second part - it's not possible to do it as you want, because you can have multiple adgroups per item_group - so I added adgroupid as an extra column. I create an array of part_numbers for the adgroup and check, using the #> operator, that all parts that are from the adgroupid are also from the unique_part_numbers.item_group_id.
WITH unique_part_numbers AS (
SELECT DISTINCT item_groups.id AS item_group_id,
part_number
FROM item_groups
JOIN item_ads ON item_group_id = item_groups.id
JOIN items ON items.id = item_ads.item_id
)
SELECT unique_part_numbers.item_group_id,
product_ads.adgroupid,
array_agg(unique_part_numbers.part_number),
SUM(clicks)
FROM unique_part_numbers
JOIN product_ads ON product_ads.part_number = unique_part_numbers.part_number
JOIN reports ON product_ads.adgroupid = reports.adgroupid
GROUP BY item_group_id, product_ads.adgroupid
HAVING array_agg(product_ads.part_number) #> (
SELECT ARRAY_AGG(other_product_ads.part_number)
FROM product_ads AS other_product_ads
WHERE other_product_ads.adgroupid = product_ads.adgroupid
)

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

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

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 - select the results of two subqueries

I have 2 complex queries that are both subqueries in postgres, the results of which are:
q1_results = id , delta , metric_1
q2_results = id , delta , metric_2
i'd like to combine the results of the queries, so the outer query can access either:
results_a = id , delta , metric_1 , metric_2
results_b = id , delta , combined_metric
i can't figure out how to do this. online searches keep leading me to UNION , but that keeps the metrics in the same column. i need to keep them split.
It's not entirely clear what you're asking in the question and the comments, but it sounds like you might be looking for a full join with a bunch of coalesce statements, e.g.:
-- create view at your option, e.g.:
-- create view combined_query as
select coalesce(a.id, b.id) as id,
coalesce(a.delta, b.delta) as delta,
a.metric1 as metric1,
b.metric2 as metric2,
coalesce(a.metric1,0) + coalesce(b.metric2,0) as combined
from (...) as results_a a
full join (...) as results_b b on a.id = b.id -- and a.delta = b.delta maybe?

How to do this JOIN

Related to my previous post here, I have the following SELECT:
SELECT tc.[Time],tc.[From], tc.[To], tc.[Cost], tc.[Length], tc.[Type], tc.[PlaceCalled]
FROM
TelstraCall as tc
WHERE
[AccountNumber] IN (#AccountNumber)
ORDER BY [Time] DESC
I'm trying to get the [Username] out of [Resource] given that the [PhoneNum] in [rtc] matches either [From] or [To], and Hogan has kindly helped me out with the first half :
USE [rtc]
SELECT [Username]
FROM [dbo].[Resource] R
JOIN ResourcePhone RP on R.ResourceId = RP.ResourceId
WHERE RP.PhoneNum = tc.[From]
Now I'm trying to work out the syntax of how to get a 'User1' given that [From] matches the [PhoneNum] in [rtc] and a 'User2' if [To] matches [PhoneNum] instead, because I can't have them being jumbled up.
What you're wanting to do is join on the same table twice to get related values based on two different references.
For this, you use table aliases. Here's a simple example
SELECT u1.[Username] AS User1, u2.[Username] AS User2
FROM TelstraCall tc
INNER JOIN ResourcePhone rp1 ON tc.[From] = rp1.PhoneNum
INNER JOIN Resource u1 ON rp1.ResourceId = u1.Id -- guessing at column names here
INNER JOIN ResourcePhone rp2 ON tc.[To] = rp2.PhoneNum
INNER JOIN Resource u2 ON rp2.ResourceId = u2.Id
Here is one way that you can do this using CROSS APPLY since you are using SQL Server 2008. CROSS APPLY helps you to join your table with sub queries.
In this case, the table CallDetails in the database PhoneBills drives your query using the fields From and To. Both these fields have to fetch the Username data from the table Resource in the database rtc by joining with the PhoneNumber column in the table ResourcePhone also in the database rtc.
So the inner/sub query will join the tables Resource and ResourcePhone, it will then be used twice to fetch User1 and User2. For User1, the filter will use the From field in the table CallDetails in the database PhoneBills and for User2, the filter will use the To field in the table CallDetails in the database PhoneBills
SELECT USR1.UserName AS [User1]
, USR2.UserName AS [User2]
FROM PhoneBills.dbo.CallDetails CD
CROSS APPLY (
SELECT Username
FROM rtc.dbo.Resource R
INNER JOIN rtc.dbo.ResourcePhone RP
ON RP.ResourceID = R.ResourceID
WHERE RP.PhoneNumber = CD.From
) USR1
CROSS APPLY (
SELECT Username
FROM rtc.dbo.Resource R
INNER JOIN rtc.dbo.ResourcePhone RP
ON RP.ResourceID = R.ResourceID
WHERE RP.PhoneNumber = CD.To
) USR2