SQL : Max value based on multiple row values - sql

Based on my table :
Client Tier Value
A 1 10
B 2 20
C 1 30
B 3 40
A 2 40
B 1 50
C 2 60
A 3 70
A 4 50
I am trying to get the following output :
Client max(1,2) Value max(3,4) Value
A 40 70
B 50 40
Using Python I can easily do , But I am new to Postgres.
So Basically for every client, I need to get Maximum of (Tier 1 Value, Tier 2 Value) as a first column and Maximum of (Tier 3 Value, Tier 4 Value) as a second column.

We can use conditional aggregation here:
SELECT Client,
MAX(CASE WHEN Tier IN (1, 2) THEN Value END) AS max_1_2,
MAX(CASE WHEN Tier IN (3, 4) THEN Value END) AS max_3_4
FROM yourTable
GROUP BY Client;
Or, using the FILTER clause, which Postgres supports in recent versions, we could also write this as:
SELECT Client,
MAX(Value) FILTER (WHERE Tier IN (1, 2)) AS max_1_2,
MAX(Value) FILTER (WHERE Tier IN (3, 4)) AS max_3_4
FROM yourTable
GROUP BY Client;

Related

case end / self-join postres sql

I am trying to process data within the same table.
Input:
Table
id sort value
1 1 1
2 1 8
3 2 0
4 1 2
What I want to achieve is obtain for each id, the first encountered value for all value equal to its sort, and this ordered by id.
Output
Table
id sort value new
1 1 1 1
2 1 8 1
3 2 0 0
4 1 2 1
I tried to self join the table, but I constantly get relation not found. I tried with a case statement but I don't see how can I connect to the same table, I get the same error, relation not found.
The beauty of SQL is that many requirements (yours included) can be verbosely described in very similar way they are finally coded:
with t(id, sort, value ) as (values
(1, 1, 1),
(2, 1, 8),
(3, 2, 0),
(4, 1, 2)
)
select t.*
, first_value(value) over (partition by sort order by id) as "new"
from t
order by id
id
sort
value
new
1
1
1
1
2
1
8
1
3
2
0
0
4
1
2
1
fiddle

Compare rows in group with specific value postgresql

Edit: when 5 or 9 does not exist, i need a null value (or another flag)
I have 3 columns. SECTION, STATUS and NAME. Within a SECTION there are a maximum of 10 rows (STATUS 1 to 10). I have to compare the value of NAME for STATUS 5 and 9 within a SECTION. AND then indicate if those 2 NAMES (for STATUS 5 and 9) are the same for each SECTION.
section status name
1 5 a
1 6 a
1 9 b
2 4 c
2 5 d
2 9 d
2 10 d
3 5 e
3 10 e
Desired output
Section equalnames
1 no
2 yes
3 null/flag
I would use a boolean instead of strings and just use aggregation with filtering:
select group, min(name) = max(name) as names_equal_flag
from t
where status in (5, 9)
group by group;
Note that group is a really bad name for a column because it is a SQL keyword. I assume you have a more appropriate name in your actual data.
If you want a string, you can use case:
select group,
(case when min(name) = max(name) then 'yes' else 'no' end) as names_equal_flag
from t
where status in (5, 9)
group by group;

SQL For each unique value in Column A, sum Column B. If sum is greater than 50, create Column C and add value "Y", else "N"

Seems simple, but I can't seem to wrap my head around it. I have the table below.
ID Total
1 20
1 30
1 30
2 10
2 10
For each unique value in 'ID' column, I want the sum of 'Total' column. If the sum of the total is greater than 50, create new column 'Result' and add value 'Y', if not then add value 'N'.
The result should look like this. Yes, I want to keep each row.
ID Total Result
1 20 Y
1 30 Y
1 30 Y
2 10 N
2 10 N
I'm stuck....
Use window functions:
select t.*,
(case when sum(total) over (partition by id) >= 50 then 'Y' else 'N' end)
from t;

Assigning a value of data for each record having the same condition in SQL Server 2008

I have a table in SQL Server 2008 like:
Period Name Value
1 A 10
2 A 20
3 A 30
4 A 40
1 B 50
2 B 80
3 B 70
4 B 60
What I need to write a select query includes a new column MainValue which contains the value where period=4 for a name for each data.
Example:
Period Name Value MainValue
1 A 10 40
2 A 20 40
3 A 30 40
4 A 40 40
1 B 50 60
2 B 80 60
3 B 70 60
4 B 60 60
How can I provide this? I tried the one below, but it is not working as I want.
Select
*,
(select Value where Period = 4) as MainValue
from myTable;
Any help would be appreciated.
Try this:
SELECT Period, Name, Value,
MAX(CASE WHEN Period=4 THEN Value END) OVER (PARTITION BY Name) AS MainValue
FROM mytable
The query uses a window function with a condition applied over Name partitions: the function returns the Value corresponding to Period=4 inside each partition.
You can do this a number of ways. A correlated sub-query as the column, a cross apply to a correlated query, or a cte. I personally like the cte approach. It would look something like this.
with MainValues as
(
select Name
, Value
from SomeTable
where Period = 4
)
select st.*
, mv.Value as MainValue
from SomeTable st
join MainValues mv on st.Name = mv.Name

How to find the SQL medians for a grouping

I am working with SQL Server 2008
If I have a Table as such:
Code Value
-----------------------
4 240
4 299
4 210
2 NULL
2 3
6 30
6 80
6 10
4 240
2 30
How can I find the median AND group by the Code column please?
To get a resultset like this:
Code Median
-----------------------
4 240
2 16.5
6 30
I really like this solution for median, but unfortunately it doesn't include Group By:
https://stackoverflow.com/a/2026609/106227
The solution using rank works nicely when you have an odd number of members in each group, i.e. the median exists within the sample, where you have an even number of members the rank method will fall down, e.g.
1
2
3
4
The median here is 2.5 (i.e. half the group is smaller, and half the group is larger) but the rank method will return 3. To get around this you essentially need to take the top value from the bottom half of the group, and the bottom value of the top half of the group, and take an average of the two values.
WITH CTE AS
( SELECT Code,
Value,
[half1] = NTILE(2) OVER(PARTITION BY Code ORDER BY Value),
[half2] = NTILE(2) OVER(PARTITION BY Code ORDER BY Value DESC)
FROM T
WHERE Value IS NOT NULL
)
SELECT Code,
(MAX(CASE WHEN Half1 = 1 THEN Value END) +
MIN(CASE WHEN Half2 = 1 THEN Value END)) / 2.0
FROM CTE
GROUP BY Code;
Example on SQL Fiddle
In SQL Server 2012 you can use PERCENTILE_CONT
SELECT DISTINCT
Code,
Median = PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY Value) OVER(PARTITION BY Code)
FROM T;
Example on SQL Fiddle
SQL Server does not have a function to calculate medians, but you could use the ROW_NUMBER function like this:
WITH RankedTable AS (
SELECT Code, Value,
ROW_NUMBER() OVER (PARTITION BY Code ORDER BY VALUE) AS Rnk,
COUNT(*) OVER (PARTITION BY Code) AS Cnt
FROM MyTable
)
SELECT Code, Value
FROM RankedTable
WHERE Rnk = Cnt / 2 + 1
To elaborate a bit on this solution, consider the output of the RankedTable CTE:
Code Value Rnk Cnt
---------------------------
4 240 2 3 -- Median
4 299 3 3
4 210 1 3
2 NULL 1 2
2 3 2 2 -- Median
6 30 2 3 -- Median
6 80 3 3
6 10 1 3
Now from this result set, if you only return those rows where Rnk equals Cnt / 2 + 1 (integer division), you get only the rows with the median value for each group.