Iterating multiple times over same table (postgres sql) - sql

i am working with sql from few days as beginner and stuck at a problem!
Problem:
Given A table user_email_table which columns are (id, user_id, user_id_email)
How to get all user co-related to each and every user extending himself
user_email_table
id | user_id | email_id
1 | 1 | xyz
2 | 2 | xyz2
3 | 3 | xyz3
4 | 4 | xyz4
Desired Output
id | user_id_1 | user_id_2 | user_id_1_email | user_id_2_email |
-----------------------------
1 | 1 | 2 | xyz|xyz2|
1 | 1 | 3 |xyz|xyz3|
1 | 1 | 4 |xyz|xyz4|
1 | 2 | 1 |xyz2|xyz1|
1 | 2 | 3 |xyz2|xyz3|
1 | 2 | 4 |xyz2|xyz4|
1 | 3 | 1 |xyz3|xyz1|
1 | 3 | 2 |xyz3|xyz2|
1 | 3 | 4 |xyz3|xyz4|
1 | 4 | 1 |xy4|xyz1|
1 | 4 | 2 |xyz4|xyz2|
1 | 4 | 3 |xyz4|xyz3|
Please Ignore validate data of email fields This are just for reminding to mention these columns in output-table
What SQL query can result into this table

You want a "cross join" of the table against itself excluding the self-matching rows. You can do:
select
1 as id,
a.user_id as user_id_1,
b.user_id as user_id_2,
a.email_id as user_id_1_email,
b.email_id as user_id_2_email
from user_email_table a
cross join user_email_table b
where a.user_id <> b.user_id
EDIT:
As #IMSoP points out the query can also use a common join with a join predicate and can be rephrased as:
select
1 as id,
a.user_id as user_id_1,
b.user_id as user_id_2,
a.email_id as user_id_1_email,
b.email_id as user_id_2_email
from user_email_table a
join user_email_table b on a.user_id <> b.user_id

Related

Postgres - Unique values for id column using CTE, Joins alongside GROUP BY

I have a table referrals:
id | user_id_owner | firstname | is_active | user_type | referred_at
----+---------------+-----------+-----------+-----------+-------------
3 | 2 | c | t | agent | 3
5 | 3 | e | f | customer | 5
4 | 1 | d | t | agent | 4
2 | 1 | b | f | agent | 2
1 | 1 | a | t | agent | 1
And another table activations
id | user_id_owner | referral_id | amount_earned | activated_at | app_id
----+---------------+-------------+---------------+--------------+--------
2 | 2 | 3 | 3.0 | 3 | a
4 | 1 | 1 | 6.0 | 5 | b
5 | 4 | 4 | 3.0 | 6 | c
1 | 1 | 2 | 2.0 | 2 | b
3 | 1 | 2 | 5.0 | 4 | b
6 | 1 | 2 | 7.0 | 8 | a
I am trying to generate another table from the two tables that has only unique values for referrals.id and returns as one of the columns the count for each apps as best_selling_app_count.
Here is the query I ran:
with agents
as
(select
referrals.id,
referral_id,
amount_earned,
referred_at,
activated_at,
activations.app_id
from referrals
left outer join activations
on (referrals.id = activations.referral_id)
where referrals.user_id_owner = 1),
distinct_referrals_by_id
as
(select
id,
count(referral_id) as activations_count,
sum(coalesce(amount_earned, 0)) as amount_earned,
referred_at,
max(activated_at) as last_activated_at
from
agents
group by id, referred_at),
distinct_referrals_by_app_id
as
(select id, app_id as best_selling_app,
count(app_id) as best_selling_app_count
from agents
group by id, app_id )
select *, dense_rank() over (order by best_selling_app_count desc) best_selling_app_rank
from distinct_referrals_by_id
inner join distinct_referrals_by_app_id
on (distinct_referrals_by_id.id = distinct_referrals_by_app_id.id);
Here is the result I got:
id | activations_count | amount_earned | referred_at | last_activated_at | id | best_selling_app | best_selling_app_count | best_selling_app_rank
----+-------------------+---------------+-------------+-------------------+----+------------------+------------------------+-----------------------
2 | 3 | 14.0 | 2 | 8 | 2 | b | 2 | 1
1 | 1 | 6.0 | 1 | 5 | 1 | b | 1 | 2
2 | 3 | 14.0 | 2 | 8 | 2 | a | 1 | 2
4 | 1 | 3.0 | 4 | 6 | 4 | c | 1 | 2
The problem with this result is that the table has a duplicate id of 2. I only need unique values for the id column.
I tried a workaround by harnessing distinct that gave desired result but I fear the query results may not be reliable and consistent.
Here is the workaround query:
with agents
as
(select
referrals.id,
referral_id,
amount_earned,
referred_at,
activated_at,
activations.app_id
from referrals
left outer join activations
on (referrals.id = activations.referral_id)
where referrals.user_id_owner = 1),
distinct_referrals_by_id
as
(select
id,
count(referral_id) as activations_count,
sum(coalesce(amount_earned, 0)) as amount_earned,
referred_at,
max(activated_at) as last_activated_at
from
agents
group by id, referred_at),
distinct_referrals_by_app_id
as
(select
distinct on(id), app_id as best_selling_app,
count(app_id) as best_selling_app_count
from agents
group by id, app_id
order by id, best_selling_app_count desc)
select *, dense_rank() over (order by best_selling_app_count desc) best_selling_app_rank
from distinct_referrals_by_id
inner join distinct_referrals_by_app_id
on (distinct_referrals_by_id.id = distinct_referrals_by_app_id.id);
I need a recommendation on how best to achieve this.
I am trying to generate another table from the two tables that has only unique values for referrals.id and returns as one of the columns the count for each apps as best_selling_app_count.
Your question is really complicated with a very complicated SQL query. However, the above is what looks like the actual question. If so, you can use:
select r.*,
a.app_id as most_common_app_id,
a.cnt as most_common_app_id_count
from referrals r left join
(select distinct on (a.referral_id) a.referral_id, a.app_id, count(*) as cnt
from activations a
group by a.referral_id, a.app_id
order by a.referral_id, count(*) desc
) a
on a.referral_id = r.id;
You have not explained the other columns that are in your result set.

Postgres SQL query to get the first row of distinct id

channels table
id | name
------------
1 | ABC
2 | XYZ
3 | MNO
4 | ASD
user_channels table
user_id | channel_id
----------------------
555 | 1
666 | 1
777 | 1
555 | 2
888 | 2
999 | 3
555 | 3
user_chats table
id | created_at | channel_id | content
---------------------------------------
2 | time 1 | 1 | Hello
3 | time 2 | 1 | Hi
4 | time 3 | 2 | Good day
5 | time 4 | 2 | Morning
I have these 3 tables in postgres SQL,
I want to write a sql query to get user_channels by user_id and it's latest message only (time 1 is oldest message) from user_chats table. How can I do that?
For example, for user_id = 555, the query should return
channel_id | content | created_at
---------------------------------------
1 | Hi | time 2
2 | Morning | time 4
3 | Null | Null
Use distinct on:
select distinct on (a.channel_id) a.*
from user_chats a
inner join user_channels l on l.channel_id = a.channel_id
where l.user_id = 555
order by a.channel_id, a.createt_at desc
If you want this for all users at once:
select distinct on (l.user_id, a.channel_id) l.user_id, a.*
from user_chats a
inner join user_channels l on l.channel_id = a.channel_id
order by l.user_id, a.channel_id, a.createt_at desc
You can use distinct on:
select distinct on (c.channel_id) c.channel_id, uc.content, uc.created_at
from user_channels c left join
user_chats uc
on uc.channel_id = c.channel_id
where c.user_id = ?
order by c.idchannel_id, uc.created_at desc;

How to do it by simple sql or procedure

object_tbl:
objId(primary key) | name
1 | A
2 | B
3 | C
document_tbl:
documentId | sourceId
1 | 2
2 | 2
3 | 1
4 | 3
5 | 3
6 | 3
objToDoc_tbl:
id | objectId | documentId
1 | 1 | 2
2 | 2 | 4
3 | 2 | 6
4 | 1 | 5
5 | 3 | 1
6 | 1 | 2
Inner join of all table
A 2 2
B 4 3
B 6 3
A 5 3
C 1 2
A 2 2
A 2
B 1
C 1
so Answer is 1(As A is only 2)
Question: - How many object are related to documents from multiple sources.
How we can write sql query for this or it can only we solved by procedure
You can join all the tables and then use having to get objects with count > 1.
with t as (
select t.name, count(distinct d.sourceid) as count
from objtodoc_tbl o
join document_tbl d on o.documentid = d.documentid
join object_tbl t on o.objectid = t.name
group by t.name
having count(distinct d.sourceid) > 1
)
select count(t.name) from t;

How to generate an order index value (as in the order of a list) in a SELECT statement

Suppose I have these two tables :
TABLEA TABLEB
----------- -----------
ID | NAME ID | TABLEA_ID | NAME
1 | ... 1 | 1 | ...
2 | 2 | 2 | ...
3 | 3 | 2 |
4 | 4 | 2 |
5 | 3 |
6 | 3 |
7 | 4 |
8 | 2 |
I want an SQL SELECT statement that can generate such result when TABLEA.ID = TABLEB.TABLEA_ID, you can note here I don't care about grouping or ordering, I just want to generate a incremented value for each line of the same TABLEB.TABLEA_ID.
ID | TABLEA_ID | ORDER_INDEX | NAME
1 | 1 | 0 | ...
2 | 2 | 0 | ...
3 | 2 | 1 |
4 | 2 | 2 |
5 | 3 | 0 |
6 | 3 | 1 |
7 | 4 | 0 |
8 | 2 | 3 |
I tried without success to use rownum in several combination of sub-selects to generate the ORDER_INDEX depending on the value in TABLEA_ID.
Do you have hint to do that in plain SQL, is it even possible with plain SQL.
Is it possible via a PL/SQL ? And how if possible ?
Thank you very much in advance.
I believe that this is what you want:
SELECT B.ID, B.TABLEA_ID,
ROW_NUMBER() OVER(PARTITION BY B.TABLEA_ID ORDER BY B.ID) - 1 ORDER_INDEX,
B.NAME -- OR A.NAME, its not clear on your question
FROM TABLEB B
LEFT JOIN TABLEA A
ON B.TABLEA_ID = A.ID
Something like this:
SELECT
TableB.ID,
TableB.TableA_ID,
ROW_NUMBER() OVER(PARTITION BY TableB.TableA_ID ORDER BY TableB.TableA_ID) AS ORDER_INDEX,
TableB.Name
FROM
TableA
JOIN TableB
ON TableA.ID=TableB.TableA_ID
ORDER BY TableB.ID
How about
ROW_NUMBER() OVER (PARTITION BY TABLEA_ID ORDER BY ID ASC) AS ORDER_INDEX
as the definition of ORDER_INDEX

display basic information from tables

I have two tables i.e.
Users
uid | firstname
1 | John
2 | Bob
3 | Paul
4 | Peter
Calls
cid | assigned_to | caller_id
1 | 2 | 1
2 | 1 | 3
3 | 2 | 4
4 | 4 | 2
assigned_to and caller_id are just the uid in users.
I just want to display the results of each call:
call_id | username(assigned_to) | username(caller_id)
How can I do this in SQL?
Thanks,
Try this:
select
cid as call_id,
A.username, -- assingned to
B.username -- caller id
from calls
left join users A on calls.assigned_to = A.uid
left join users B on calls.caller_id = B.uid