Aggregating columns inside a CASE statement - sql

I have a case such that
~id ~from ~to ~label ~weight
100 A B knows 2
100 A B knows 3
100 A B knows 4
But I want only the weight for maximum Date.
How can I modify the below CASE statement such that only 1 entry is there for an ID.
Query:
(
select distinct
CASE WHEN *some-condition* as "~id"
,CASE *some-condition* as "~from"
,CASE *some-condition* as "~to"
,CASE *some-condition* as "~label"
,CASE ??? as "weight"
from
(select
dense_rank() over(partition by t.job_id order by start_time desc) rnk,
t.Date,
t.job_id,
t.start_time,
t.end_time,
t.dep_id,
t.table_name
.....
t.region_id,
from Table1 t
,Tabel2 J
where t.JOB_ID=J.JOB_ID
)
where rnk=1
order by JOB_ID,table_name
)
where "~id" is NOT NULL and "~label" is NOT NULL and "~from" is NOT NULL and "~to" is NOT NULL;
;
Table t
job_id Date table_name ....... dep_id weight
100 2020-10-20 abc 1 2
100 2020-10-20 abc 2 3
100 2020-10-20 abc 3 4
100 2020-10-20 abc 4 10
100 2020-10-19 abc 3 2
Output weight in the result should be corresponding to maximum dep_id.
~id ~from ~to ~label ~weight
100 A B knows 10

It's quite hard to come up with a solution since you didn't state how ~id, ~from, ~to, ~label are calculated. You should be able to achieve your desired output with window functions, i.e. FIRST_VALUE():
...
,CASE *some-condition* as "~label"
,FIRST_VALUE(weight)OVER(ORDER BY dep_id desc) "weight"
...
You may need to add a PARTITION BY clause depending if you want to have the first value overall or depending on some other conditions as well.

Related

Select rows from a particular row to latest row if that particular row type exist

I want to achieve these two requirements using a single query. Currently I'm using 2 queries in the program and use C# to do the process part something like this.
Pseudocode
select top 1 id from table where type=b
if result.row.count > 0 {var typeBid = row["id"]}
select * from table where id >= {typeBid}
else
select * from table
Req1: If there is records exist with type=b, Result should be latest row with type=b and all other rows added after.
Table
--------------------
id type date
--------------------
1 b 2021-10-15
2 a 2021-11-16
3 b 2021-11-19
4 a 2021-12-02
5 c 2021-12-12
6 a 2021-12-16
Result
--------------------
id type date
--------------------
3 b 2021-11-19
4 a 2021-12-02
5 c 2021-12-12
6 a 2021-12-16
Req2: There is NO record exist with type=b. Query should select all the records in the table
Table
---------------------
id type date
---------------------
1 a 2021-10-15
2 a 2021-11-16
3 a 2021-11-19
4 a 2021-12-02
5 c 2021-12-12
6 a 2021-12-16
Result
--------------------
id type date
--------------------
1 a 2021-10-15
2 a 2021-11-16
3 a 2021-11-19
4 a 2021-12-02
5 c 2021-12-12
6 a 2021-12-16
with max_b_date as (select max(date) as date
from table1 where type = 'b')
select t1.*
from table1 t1
cross join max_b_date
where t1.date >= max_b_date.date
or max_b_date.date is null
(table is a SQL reserved word, https://en.wikipedia.org/wiki/SQL_reserved_words, so I used table1 as table name instead.)
https://dbfiddle.uk/?rdbms=sqlserver_2019&fiddle=bd05543a9712e27f01528708f10b209f
Please try this(It's somewhat deep but might you exact looking for)
select ab.* from
((select top 1 id, type, date from test where type = 'b' order by id desc)
union
select * from test where type != 'b') as ab
where ab.id >= (select COALESCE((select top 1 id from test where type = 'b' order by id desc), 0))
order by ab.id;
https://dbfiddle.uk/?rdbms=sqlserver_2019&fiddle=739eb6bfee787e5079e616bbf4e933b1
Looks Like you can use an OR condition here
SELECT
*
FROM
(
SELECT
*,
BCount = COUNT(CASE type WHEN 'B' THEN 1 ELSE NULL END)-- to get the Count of Records with Type b.
FROM Table
)Q
WHERE
(
BCount > 0 AND id >= (select top 1 id from table where type=b)-- if there are Row's with Type b then select Req#1
)
OR
(
BCount = 0 -- if THere are no rows with Type B select All
)

Retrieving last record in each group from database with order by

There is a table ticket that contains data as shown below:
Id Impact group create_date
------------------------------------------
1 3 ABC 2020-07-28 00:42:00.0
1 2 ABC 2020-07-28 00:45:00.0
1 3 ABC 2020-07-28 00:48:00.0
1 3 ABC 2020-07-28 00:52:00.0
1 3 XYZ 2020-07-28 00:55:00.0
1 3 XYZ 2020-07-28 00:59:00.0
Expected result:
Id Impact group create_date
------------------------------------------
1 3 ABC 2020-07-28 00:42:00.0
1 2 ABC 2020-07-28 00:45:00.0
1 3 ABC 2020-07-28 00:52:00.0
1 3 XYZ 2020-07-28 00:59:00.0
At present, this is the query that I use:
WITH final AS (
SELECT p.*,
ROW_NUMBER() OVER(PARTITION BY p.id,p.group,p.impact
ORDER BY p.create_date desc, p.impact) AS rk
FROM ticket p
)
SELECT f.*
FROM final f
WHERE f.rk = 1
Result, i am getting is:
Id Impact group create_date
-----------------------------------------
1 2 ABC 2020-07-28 00:45:00.0
1 3 ABC 2020-07-28 00:52:00.0
1 3 XYZ 2020-07-28 00:59:00.0
it seems that partition by is getting precedence over order by values. is there other way to achieve expected result. I am running these queries on amazon Redshift.
You could use LEAD() to check if the Impact changes between rows, taking only the rows where the value will change.
WITH
look_forward AS
(
SELECT
*,
LEAD(impact) OVER (PARTITION BY id, group ORDER BY create_date) AS lead_impact
FROM
ticket
)
SELECT
*
FROM
look_forward
WHERE
lead_impact IS NULL
OR lead_impact <> impact
You seem to want rows where id/impact/group change relative to the next row. A simple way is to look at the next create_date overall and the next create_date for the group. If these are the same, then filter:
select t.*
from (select t.*,
lead(create_date) over (order by create_date) as next_create_date,
lead(create_date) over (partition by id, impact, group order by create_date) as next_create_date_img
from ticket t
) t
where next_create_date_img is null or next_create_date_img <> next_create_date;

Postgres query with limit that selects all records with similar identifier

I have a table that looks something like this:
customer_id
data
1
123
1
456
2
789
2
101
2
121
2
123
3
123
4
456
What I would like to do is perform a SELECT combined with a LIMIT X to get X number of records as well as any other records that have the same customer_id
Example query: SELECT customer_id, data FROM table ORDER BY customer_id LIMIT 3;
This query returns:
customer_id
data
1
123
1
456
2
789
I'd like a query that will look at the last customer_id value and return all remaining records that match beyond the LIMIT specified. Is it possible to do this in a single operation?
Desired output:
customer_id
data
1
123
1
456
2
789
2
101
2
121
2
123
In Postgres 13 can use with ties:
select t.*
from t
order by customer_id
fetch first 3 rows with ties;
In earlier versions you can use in:
select t.*
from t
where t.customer_id in (select t2.customer_id
from t t2
order by t2.customer_id
limit 3
);
You can use corelated subquery with count as follows:
Select t.*
From t
Where 3 >= (select count(distinct customer_id)
From t tt
where t.customer_id >= tt.customer_id)

Update column value of one row from other rows

I have the following table:
sno name pid amount total
1 Arif 0 100 null
2 Raj 1 200 null
3 Ramesh 2 100 null
4 Pooja 2 100 null
5 Swati 3 200 null
6 King 4 100 null
I want total of each person such that it gives total sum of amount of its descendants.
For ex.
for RAJ total will be : total= amount of(raj+ramesh+pooja+swati+king)
for SWATI :Total=amount of swati only.
You could try something like this:
WITH hierarchified AS (
SELECT
sno,
amount,
hierarchyID = CAST(sno AS varchar(500))
FROM yourTable
WHERE pid = 0
UNION ALL
SELECT
t.sno,
t.amount,
hierarchyID = CAST(h.hierarchyID + '/' + RTRIM(t.sno) AS varchar(500))
FROM yourTable t
INNER JOIN hierarchified h ON t.pid = h.sno
)
UPDATE yourTable
SET total = t.amount + ISNULL(
(
SELECT SUM(amount)
FROM hierarchified
WHERE hierarchyID LIKE h.hierarchyID + '/%'
),
0
)
FROM yourTable t
INNER JOIN hierarchified h ON t.sno = h.sno;
Note that this query (which you can try on SQL Fiddle) would probably not be very efficient on a large dataset. It might do as a one-off query, and then it would likely be better to organise updating the totals each time the table is updated, i.e. using triggers.

SQL Query: SUM on three columns with criteria

I have a table with columns like these :
idx | amount | usercol1 | usercol2 | usercol3 | percentage1 | percentage2 | percentage3
Data is typically like this :
0 | 1500 | 1 | null | null | 100 | null | null
1 | 3000 | 2 | 3 | null | 50 | 50 | null
I would like to make a SUM() of every user's amount.
Example :
user1= 1500*100/100 (amount*usercol1/100)
user2= 3000*50/100 (amount*usercol1/100)
user3= 3000*50/100 (amount*usercol2/100)
I tried UNION to no avail (did not sum the SUMs).
Is there a way to do this ? The problem being that it should GROUP BY the username (which I get with a LEFT OUTER JOIN usernames ON exampletable.usercol1=usernames.idx).
I know this is non standard and would be better with relations from another table. But I am not allowed to change the table structure.
Many many many thanks ! :=)
Hereby, an example that gives a wrong result (seems to give only results from the query in the middle)
(
SELECT SUM(projects.amount * (projects.percentage1/100)) as totalproj,
entities.idx as idx,
COUNT(projects.idx) as numproj,
entities.name
FROM projects
INNER JOIN entities ON projects.usercol1=entities.idx
WHERE projects.usercol1=entities.idx
GROUP BY name ORDER BY totalproj DESC
)
UNION ALL
(
SELECT SUM(projects.amount * (projects.percentage2/100)) as totalproj,
entities.idx as idx,
COUNT(projects.idx) as numproj,
entities.name
FROM projects
INNER JOIN entities ON projects.usercol2=entities.idx
WHERE projects.usercol2=entities.idx
GROUP BY name ORDER BY totalproj DESC
)
UNION ALL
(
SELECT SUM(projects.amount * (projects.percentage3/100)) as totalproj,
entities.idx as idx,
COUNT(projects.idx) as numproj,
entities.name
FROM projects
INNER JOIN entities ON projects.usercol3=entities.idx
WHERE projects.usercol3=entities.idx
GROUP BY name ORDER BY totalproj DESC
)
ORDER BY totalproj DESC
LIMIT 10
You could use a derived table to simulate a first normal form table then join onto that.
SELECT SUM(P.amount * (P.percentage/100)) as totalproj,
entities.idx as idx,
COUNT(P.idx) as numproj,
entities.name
FROM
(
SELECT idx, amount, usercol1 AS usercol, percentage1 AS percentage
FROM projects
UNION ALL
SELECT idx, amount, usercol2 AS usercol, percentage2 AS percentage
FROM projects
UNION ALL
SELECT idx, amount, usercol3 AS usercol, percentage3 AS percentage
FROM projects
) P
INNER JOIN entities ON P.usercol=entities.idx
WHERE P.usercol=entities.idx
GROUP BY name
ORDER BY totalproj DESC
using this data (i added some stranger data to make sure the math was working properly)
0 1500 1 NULL NULL 100 NULL NULL
1 3000 2 3 NULL 50 50 NULL
2 780 4 1 3 70 20 50
3 3800 2 4 1 30 20 10
i got these results
user commission
------- -------------
1 2036
2 2640
3 1890
4 1306
is this what you were looking for? below is my query
SELECT [user]
,SUM([commission]) AS commission
FROM ( SELECT [usercol1] AS [user]
,( [amount] * [percentage1] ) / 100 AS commission
FROM [dbo].[projects]
WHERE [usercol1] IS NOT NULL
AND [percentage1] IS NOT NULL
UNION ALL
SELECT [usercol2]
,( [amount] * [percentage2] ) / 100
FROM [dbo].[projects]
WHERE [usercol2] IS NOT NULL
AND [percentage2] IS NOT NULL
UNION ALL
SELECT [usercol3]
,( [amount] * [percentage3] ) / 100
FROM [dbo].[projects]
WHERE [usercol3] IS NOT NULL
AND [percentage3] IS NOT NULL
) x
GROUP BY [user]