Select the latest message thread values from a table using sql - sql

This is my table
Id | ReceiverId | SenderId | Text
-----------+---------------+--------------+-----------
1 | 5 | 1 | text
2 | 5 | 1 | text
3 | 1 | 5 | text
4 | 2 | 5 | text
5 | 2 | 5 | text
6 | 5 | 3 | text
7 | 5 | 4 | text
9 | 5 | 6 | text
10 | 5 | 4 | text
11 | 10 | 5 | text
12 | 5 | 10 | text
13 | 10 | 5 | text
14 | 5 | 10 | text
How do I select a row with out duplication based on [ReceiverId, SenderId] pair and Ordered by Id in Descending order. That is: [5, 1]=[1,5] are duplicate. OR [5,1] = [5,1] are also the duplicate.
So the final result should be:
Id | ReceiverId | SenderId | Text
-----------+---------------+--------------+-----------
14 | 5 | 10 | text
10 | 5 | 4 | text
9 | 5 | 6 | text
6 | 5 | 3 | text
5 | 2 | 5 | text
3 | 1 | 5 | text

Assuming that among records, which you consider to be the same by just checking the SenderId and ReceiverId (order doesn't matter), you want the one with the largest Id (which could probably be the latest). Then, this query will give you the result:
select Id, ReceiverId, SenderId, [Text]
from MyTable t
where t.Id in (
select top 1 tt.Id
from MyTable tt
where (tt.SenderId = t.SenderId and tt.ReceiverId = t.ReceiverId) or
(tt.SenderId = t.ReceiverId and tt.ReceiverId = t.SenderId)
order by tt.Id desc
)
order by t.Id desc
Replace MyTable with your table's name.

select b.ID,
a.senderid_final,
a.receiverid_final,
b.Text
from
(
select a.receiverid as a_receiverid,
a.senderid as a_senderid ,
b.receiverid as b_receiverid,
b.senderid as b_senderid,
case when max(a.id) > max (b.id) then a.receiverid else b.receiverid end as receiverid_final,
case when max(a.id) > max (b.id) then a.senderid else b.senderid end as senderid_final
from my_tables as a
inner join my_table as b
on a.receiverid = b.senderid
and b.receiverid = a.senderid
group by a.receiverid, a.senderid, b.receiverid, b.senderid
) as a
inner join my_tables as b
on a.receiverid_final = b.receiverid
and b.senderid = a.senderid_final
Order by b.id desc

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.

SQL, How to order the result of a query with precedence constraints

I have two tables with a relation 1-n. I'm looking for the way to order my elements. I could use a column "position(int)" but i prefer to use the id of the precedence :
+-------+---------------+-------+
| pk_id | precedence_id | fk_id |
+-------+---------------+-------+
| 4 | 1 | 10 |
+-------+---------------+-------+
| 1 | 6 | 10 |
+-------+---------------+-------+
| 2 | 5 | 40 |
+-------+---------------+-------+
| 3 | NULL | 10 |
+-------+---------------+-------+
| 6 | 3 | 10 |
+-------+---------------+-------+
| 5 | NULL | 40 |
+-------+---------------+-------+
I have a primary key (pk_id), a foreign key (fk_id) and a precedence constraint on the same table (precedence_id).
I am looking for the query to get the result with the precedence :
+-------+---------------+-------+
| pk_id | precedence_id | fk_id |
+-------+---------------+-------+
| 3 | NULL | 10 |
+-------+---------------+-------+
| 6 | 3 | 10 |
+-------+---------------+-------+
| 1 | 6 | 10 |
+-------+---------------+-------+
| 4 | 1 | 10 |
+-------+---------------+-------+
| 5 | NULL | 40 |
+-------+---------------+-------+
| 2 | 5 | 40 |
+-------+---------------+-------+
SELECT *
FROM tb
ORDER BY fk_id, ??
This is working properly :
WITH RECURSIVE recursive(pk_id, precedence_id, position) AS (
SELECT pk_id, precedence_id, 0
FROM tb
WHERE precedence_id ISNULL
UNION ALL
SELECT v.pk_id, v.precedence_id, rec.position + 1
FROM
tb v
INNER JOIN recursive rec ON rec.pk_id = v.precedence_id
)
SELECT tst.*, rec.position
FROM
recursive rec
INNER JOIN tb tst ON rec.pk_id = tst.pk_id
ORDER BY tst.fk_id, rec.position;
with recursive t (pk_id,path,L_name) as (
select pk_id
,array[pk_id]
,coalesce(precedence_id,0) as L_tree -- can be representative relation_name
,precedence_id
,fk_id
from tb
where coalesce(precedence_id,0) = 0
union all
select el.pk_id
,t.path || array[el.pk_id]
,coalesce(el.precedence_id,0) as L_tree -- can be representative relation_name
,coalesce(el.precedence_id,0) as precedence_id
,el.fk_id
from tb as el
join t on (el.precedence_id= t.pk_id)
where el.pk_id <> any (t.path))
select pk_id
,cast(case when array_upper(path,1)>1 then ' ' else '' end || repeat(' ', array_upper(path,1)-2)||L_name as character varying(1000)) as L_tree
,precedence_id
,array_upper(path,1) as tree_level
,fk_id from t
order by path
Try using recursive with.
I dug this up from one of my old projects.
Edit:
Found a similar example hierarchical data in postgres
use precedence_id in order by clause
select * from tb
order by fk_id,precedence_id desc,pk_id
http://sqlfiddle.com/#!17/ba4b8/5

Efficient ROW_NUMBER increment when column matches value

I'm trying to find an efficient way to derive the column Expected below from only Id and State. What I want is for the number Expected to increase each time State is 0 (ordered by Id).
+----+-------+----------+
| Id | State | Expected |
+----+-------+----------+
| 1 | 0 | 1 |
| 2 | 1 | 1 |
| 3 | 0 | 2 |
| 4 | 1 | 2 |
| 5 | 4 | 2 |
| 6 | 2 | 2 |
| 7 | 3 | 2 |
| 8 | 0 | 3 |
| 9 | 5 | 3 |
| 10 | 3 | 3 |
| 11 | 1 | 3 |
+----+-------+----------+
I have managed to accomplish this with the following SQL, but the execution time is very poor when the data set is large:
WITH Groups AS
(
SELECT Id, ROW_NUMBER() OVER (ORDER BY Id) AS GroupId FROM tblState WHERE State=0
)
SELECT S.Id, S.[State], S.Expected, G.GroupId FROM tblState S
OUTER APPLY (SELECT TOP 1 GroupId FROM Groups WHERE Groups.Id <= S.Id ORDER BY Id DESC) G
Is there a simpler and more efficient way to produce this result? (In SQL Server 2012 or later)
Just use a cumulative sum:
select s.*,
sum(case when state = 0 then 1 else 0 end) over (order by id) as expected
from tblState s;
Other method uses subquery :
select *,
(select count(*)
from table t1
where t1.id < t.id and state = 0
) as expected
from table t;

Finding nth row using sql

select top 20 *
from dbo.DUTs D
inner join dbo.Statuses S on d.StatusID = s.StatusID
where s.Description = 'Active'
Above SQL Query returns the top 20 rows, how can I get a nth row from the result of the above query? I looked at previous posts on finding the nth row and was not clear to use it for my purpose.
Thanks.
The row order is arbitrary, so I would add an ORDER BY expression. Then, you can do something like this:
SELECT TOP 1 * FROM (SELECT TOP 20 * FROM ... ORDER BY d.StatusID) AS d ORDER BY d.StatusID DESC
to get the 20th row.
You can also use OFFSET like:
SELECT * FROM ... ORDER BY d.StatusID OFFSET 19 ROWS FETCH NEXT 1 ROWS ONLY
And a third option:
SELECT * FROM (SELECT *, rownum = ROW_NUMBER() OVER (ORDER BY d.StatusID) FROM ...) AS a WHERE rownum = 20
I tend to use CTEs with the ROW_NUMBER() function to get my lists numbered in order. As #zambonee said, you'll need an ORDER BY clause either way or SQL can put them in a different order every time. It doesn't usually, but without ordering it yourself, you're not guaranteed to get the same thing twice. Here I'm assuming there's a [DateCreated] field (DATETIME NOT NULL DEFAULT GETDATE()), which is usually a good idea so you know when that record was entered. This says "give me everything in that table and add a row number with the most recent record as #1":
; WITH AllDUTs
AS (
SELECT *
, DateCreatedRank = ROW_NUMBER() OVER(ORDER BY [DateCreated] DESC)
FROM dbo.DUTs D
INNER JOIN dbo.Statuses S ON D.StatusID = S.StatusID
WHERE S.Description = 'Active'
)
SELECT *
FROM AllDUTs
WHERE AllDUTs.DateCreatedRank = 20;
SELECT * FROM (SELECT * FROM EMP ORDER BY ROWID DESC) WHERE ROWNUM<11
It's another sample:
SELECT * ,CASE WHEN COUNT(0)OVER() =ROW_NUMBER()OVER(ORDER BY number) THEN 1 ELSE 0 END IsNth
FROM (
select top 10 *
from master.dbo.spt_values AS d
where d.type='P'
) AS t
+------+--------+------+-----+------+--------+-------+
| name | number | type | low | high | status | IsNth |
+------+--------+------+-----+------+--------+-------+
| NULL | 0 | P | 1 | 1 | 0 | 0 |
| NULL | 1 | P | 1 | 2 | 0 | 0 |
| NULL | 2 | P | 1 | 4 | 0 | 0 |
| NULL | 3 | P | 1 | 8 | 0 | 0 |
| NULL | 4 | P | 1 | 16 | 0 | 0 |
| NULL | 5 | P | 1 | 32 | 0 | 0 |
| NULL | 6 | P | 1 | 64 | 0 | 0 |
| NULL | 7 | P | 1 | 128 | 0 | 0 |
| NULL | 8 | P | 2 | 1 | 0 | 0 |
| NULL | 9 | P | 2 | 2 | 0 | 1 |
+------+--------+------+-----+------+--------+-------+

Select unique results and null

I need get all lines from table, that have unique value in certain fields and all lines, than have null in this fields. Example:
id | name | group
-----------------
1 | One | 1
2 | Two | null
3 | Three| 3
4 | Four | 2
5 | Five | 1
6 | Six | 2
7 | Seven| null
Result:
id | name | group
-----------------
1 | One | 1
2 | Two | null
3 | Three| 3
4 | Four | 2
7 | Seven| null
How to make it in one request?
select t.id, t.name, t.`group`
from tablename t
join (select `group`, min(id) as mid
from tablename
where `group` is not null
group by `group`) x on x.mid = t.id and x.`group` = t.`group`
union all
select id, name, `group`
from tablename
where `group` is null