PostgreSQL - finding and updating multiple records - sql

I have a table:
ID | rows | dimensions
---+------+-----------
1 | 1 | 15 x 20
2 | 3 | 2 x 10
3 | 5 | 23 x 33
3 | 7 | 15 x 23
4 | 2 | 12 x 32
And I want to have something like that:
ID | rows | dimensions
---+------+-----------
1 | 1 | 15 x 20
2 | 3 | 2 x 10
3a | 5 | 23 x 33
3b | 7 | 15 x 23
4 | 2 | 12 x 32
How can I find the multiple ID value to make it unique?
How can I update the parent table after?
Thanks for your help!

with stats as (
SELECT "ID",
"rows",
row_number() over (partition by "ID" order by rows) as rn,
count(*) over (partition by "ID") as cnt
FROM Table1
)
UPDATE Table1
SET "ID" = CASE WHEN s.cnt > 1 THEN s."ID" || '-' || s.rn
ELSE s."ID"
END
FROM stats s
WHERE S."ID" = Table1."ID"
AND S."rows" = Table1."rows"
I'm assuming you cant have two rows with same ID and same rows other wise you need to include "dimensions" on the WHERE too.
In this case the output is

Related

Grouping the data by aggregational condition

I need to compose a query (one transaction) that will group elements and transform element's data into groups by sum of values with minimum sum value - 10 for example. If a value is equal to or greater of 10 it should be a separate group.
ElementId | GroupId | Value
1 | NULL | 12
2 | NULL | 10
3 | NULL | 9
4 | NULL | 13
5 | NULL | 25
GroupId | Sum
empty
Element with id 3 can be grouped with any other element, but ideally with another value that fewer than 10. Also, I need to update the relationship between elements and groups
After the query execution:
ElementId | GroupId | Value
1 | 1 | 12
2 | 2 | 10
3 | 3 | 9
4 | 3 | 13
5 | 4 | 25
GroupId | Sum
1 | 12
2 | 10
3 | 22 (13 + 9)
4 | 25
Any ideas?
You can use analytical function (for given sample data) as follows:
Select groupid_new as groupid,
Sum(value) as value
From
(Select t.*,
Sum(case when value >= 10 then value end) over (order by elementid) as groupid_new
From your_table t) t
Group by groupid_new

SQL subquery: if condition is true take take all rows for this "group" if it's false then take non

I have data of the following format as a result of a first query.
Now i need to add second query with a condition, which is hard for me and maybe someone can help me out with this one.
The condition is:
If the type for the customers row with the highest product_id is Y, then take all rows for given customer, if it is X then take non.
So I need to somehow select for each customer row with highest product_id, check if in that row type is Y, and then regarding on the result take all or non of the given customer data.
index customer product_id type
-----------------------------------------
1 1 51 X
2 1 42 Y
3 1 11 X
4 2 4 Y
5 2 2 Y
6 3 41 Y
7 3 22 Y
8 3 21 X
9 3 20 X
10 4 16 X
11 4 15 Y
12 4 14 Y
13 4 13 Y
14 4 12 Y
So in the example above I'd like to return rows: 4,5,6,7,8,9
Use FIRST_VALUE() window function:
SELECT [index], [customer], [product_id], [type]
FROM (
SELECT *, FIRST_VALUE([type]) OVER (PARTITION BY [customer] ORDER BY [product_id] DESC) [value]
FROM tablename
) t
WHERE [value] = 'Y'
See the demo.
Results:
> index | customer | product_id | type
> ----: | -------: | ---------: | :---
> 4 | 2 | 4 | Y
> 5 | 2 | 2 | Y
> 6 | 3 | 41 | Y
> 7 | 3 | 22 | Y
> 8 | 3 | 21 | X
> 9 | 3 | 20 | X

select only tuples where second column always has same value

I have a similar table to this one
ID | CountryID
1 | 22
1 | 22
2 | 19
3 | 0
3 | 14
3 | 18
3 | 21
3 | 22
3 | 23
4 | 19
5 | 9
5 | 9
6 | 14
and I want to group by the first ID column but select only rows, where the CountryID has the same value throughout an ID. The resulting table should look like
ID | CountryID
1 | 22
2 | 19
4 | 19
5 | 9
6 | 14
Any ideas?
I think the following query should work:
SELECT ID, MAX(CountryID)
FROM Table1
GROUP BY ID
HAVING MIN(CountryID) = MAX(CountryID)
SELECT ID, count(distinct CountryID)
FROM Table1
GROUP BY ID
HAVING count(distinct CountryID)=1

SQL Query to select each row with max value per group

I'm very new to SQL and this one has me stumpted. Can you help me out with this query?
I have the following 2 tables:
TABLE 1: IssueTable
Id | RunId | Value
---
1 | 1 | 10
2 | 1 | 20
3 | 1 | 30
4 | 2 | 40
5 | 2 | 50
6 | 3 | 60
7 | 4 | 70
8 | 5 | 80
9 | 6 | 90
TABLE 2: RunTable
RunId | EnvironmentId
---
1 | 1
2 | 3
3 | 1
4 | 2
5 | 4
6 | 2
I need the IssueTable rows that represent the Max RunId grouped by the EnvironmentId in the RunTable. The result I would need from the tables is:
EXPECTED RESULT:
Id | RunId | Value | EnvironmentId
---
4 | 2 | 40 | 3
5 | 2 | 50 | 3
6 | 3 | 60 | 1
8 | 5 | 80 | 4
9 | 6 | 90 | 2
So only the rows with the most recent/highest RunId from the RunTable per EnvironmentId. For example, for the EnvironmentId of "1", I only want rows that contain a RunId of "3" because the most recent RunId on EnvironmentId "1" from the RunTable is "3". Likewise, the most recent run for EnvironementId "2" was RunId "6"
Use a subquery to get the max runid for each environmentid from the runtable. Join the obtained result to the issuetable and select the required columns.
select i.id, i.runid, i.value, r.environmentid
from (select environmentid, max(runid) maxrunid
from runtable
group by environmentid) r
join issuetable i on i.runid = r.maxrunid
order by i.runid, i.id
These days one can use the analytical functions like RANK, DENSE_RANK, ROW_NUMBER to generate some ranking of your records.
Window functions are part of the ANSI SQL:2003 standard.
And I've at least encountered them on TeraData, Oracle and SQL-Server.
select Id, RunId, Value, EnvironmentId
from (
select i.*, r.EnvironmentId,
dense_rank() over (partition by r.EnvironmentId order by r.RunId desc) as RN
from issuetable i
inner join runtable r on (i.RunId = r.RunId)
) Q
where RN = 1
order by Id;
The inner query would yield the following results :
Id RunId Value EnvironmentId RN
1 1 10 1 2
2 1 20 1 2
3 1 30 1 2
4 2 40 3 1
5 2 50 3 1
6 3 60 1 1
7 4 70 2 2
8 5 80 4 1
9 6 90 2 1

Can I order by multiple columns and somehow keep the ordering related between columns in MySQL?

I know the title doesn't explain my question very well (if someone can come up with a better title then please edit it). Here's what I want to do, say I have the following table:
id | a | b | c
------------------
1 | 3 | 3 | 3
2 | 20 | 40 | 30
3 | 40 | 30 | 10
4 | 30 | 10 | 15
5 | 10 | 15 | 6
6 | 15 | 6 | 20
This is slightly truncated version, I have a few more columns to sort by, but the principle behind the data & my question is the same.
What I would like is to get the data ordered in the following way:
The row with the highest value in col a
The row with the highest value in col b
The row with the highest value in col c
Followed by all remaining rows ordered by their value in col c
So, the result set would look like:
id | a | b | c
------------------
3 | 40 | 30 | 10
2 | 20 | 40 | 30
6 | 15 | 6 | 20
4 | 30 | 10 | 15
5 | 10 | 15 | 6
1 | 3 | 3 | 3
Doing a
SELECT id, a, b, c
FROM table
ORDER BY a DESC, b DESC, c DESC
Obviously gives me a ordered first, then b and finally c, so the following (which is not what I need):
id | a | b | c
------------------
3 | 40 | 30 | 10
4 | 30 | 10 | 15
2 | 20 | 40 | 30
6 | 15 | 6 | 20
5 | 10 | 15 | 6
1 | 3 | 3 | 3
I'm not familiar with the MySQL TSQL dialect but you would have to first SELECT the row with the highest 'A' value, perform a UNION ALL (i.e. no distinct via sorting) with the row with the highest 'B' value, perform a UNION ALL with the row with the highest 'C' value and then a UNION ALL with the remaining rows ordered by 'C' and excluding the 3 rows (by id) already selected.
I've just tested the following which appears to work (does involve 3 subqueries however):
SELECT id, a, b, c
FROM test
ORDER BY FIELD(a,(SELECT MAX(a) FROM test)) DESC,
FIELD(b,(SELECT MAX(b) FROM test)) DESC,
FIELD(c,(SELECT MAX(c) FROM test)) DESC,
c DESC