How to use joins and sum() in SQL Server query - sql

I have two tables Items and Transactions. In the items table, all the items are listed. In the transactions table it is where a particular employee can request for an item depending on the quantity that he/she requested.
How to use joins to merge the data from two tables that will compute for the balance quantity of each item?
Note: (Quantity Balance = Quantity - TR_Qty)
ITEMS table:
| ID | ITEM | UNIT | QUANTITY | PRICE |
| 1 | Perfume | btl. | 50 | 200.00 |
| 2 | Battery | pc. | 100 | 25.00 |
| 3 | Milk | can | 250 | 70.00 |
| 4 | Soap | pack | 400 | 150.00 |
TRANSACTIONS table:
| ID | ITEM_ID | TR_QTY | REQUSETOR | PROCESSOR | Date |Time |
| 1 | 1 | 20 | A. Jordan | K. Koslav | 12-22-2014 |09:00|
| 2 | 2 | 8 | B. Wilkins | Z. Flores | 12-22-2014 |10:03|
| 3 | 3 | 80 | C. Potran | A. Mabag | 12-26-2014 |14:23|
| 4 | 3 | 45 | D. Korvak | D. Sanchez | 12-28-2014 |15:33|
| 5 | 4 | 22 | C. Carvicci | A. Flux | 12-31-2014 |16:02|
| 6 | 1 | 18 | F. Sansi | N. Mahone | 01-22-2015 |08:45|
| 7 | 4 | 14 | Z. Gorai | M. Sucre | 01-30-2015 |16:33|
| 8 | 2 | 7 | L. ZOnsey | P. Panchito | 02-11-2015 |17:22|
Desired output:
| ID | ITEM | QUANITY BALANCE|
| 1 | Perfume | 462 |
| 2 | Battery | 85 |
| 3 | Milk | 125 |
| 4 |Soap | 364 |

Try this:
DECLARE #Items TABLE(ID INT, Item NVARCHAR(10), Q INT)
DECLARE #Transactions TABLE(ID INT, ItemID INT, TQ INT)
INSERT INTO #Items VALUES
(1, 'Perfume', 500),
(2, 'Battery', 100),
(3, 'Milk', 250),
(4, 'Soap', 400)
INSERT INTO #Transactions VALUES
(1, 1, 20),
(2, 2, 8),
(3, 3, 80),
(4, 3, 45),
(5, 4, 22),
(6, 1, 18),
(7, 4, 14),
(8, 2, 7)
SELECT i.ID, i.Item, MAX(i.Q) - ISNULL(SUM(t.TQ), 0) AS Balance
FROM #Items i
LEFT JOIN #Transactions t ON t.ItemID = i.ID
GROUP BY i.ID, i.Item
ORDER BY i.ID
Output:
ID Item Balance
1 Perfume 462
2 Battery 85
3 Milk 125
4 Soap 364

You can do it for example by using outer apply and creating the sum of quantities in there:
select
I.ID,
I.ITEM,
I.QUANTITY - isnull(T.QUANTITY, 0) as BALANCE
from
ITEMS I
outer apply (
select sum(TR_QTY) as QUANTITY
from TRANSACTIONS T
where T.ITEM_ID = I.ID
) T

SELECT ITEM , ( SELECT (SUM(TRANSACTIONS.TR_QTY)-ITEMS.TR_QTY) FROM TRANSACTIONS WHERE TRANSACTIONS.ITEM_ID = ITEMS.ID ) AS QUANITY BALANCE FROM ITEMS
Field name and table name is as you mentioned in query ( you should change that as space is not valid for field or table name )

Related

Kudu Conditional UPSERT INTO

Does Kudu support conditions on the UPDATE portion of UPSERT INTO?
Can I provide a conditional clause to only update given values based on a comparison between the insert values and destination table?
The actual use case is to update a timestamp column with the latest.
Here's the behavior as I imagine it.
CREATE TABLE my_first_table
(
id INT,
name STRING,
status INT,
PRIMARY KEY(id)
)
PARTITION BY HASH PARTITIONS 4
STORED AS KUDU;
INSERT INTO my_first_table VALUES (1, "lee", 101), (2 "shiv", 102), (3,"bob", 103);
--CONDITION FALSE, UPDATE NOT PERFORMED
UPSERT INTO my_first_table AS t
VALUES (3, "bobby", 100) AS v
WHERE v.status > t.status
+----+------+--------+
| id | name | status |
+----+------+--------+
| 1 | lee | 101 |
| 2 | shiv | 102 |
| 3 | bob | 103 |
+----+------+--------+
--CONDITION TRUE, UPDATE PERFORMED
UPSERT INTO my_first_table AS t
VALUES (3, "bobby", 100) AS v
WHERE v.status < t.status
+----+------+--------+
| id | name | status |
+----+------+--------+
| 1 | lee | 101 |
| 2 | shiv | 102 |
| 3 | bobby| 100 |
+----+------+--------+
In the case where 3 does not exist, it should insert.
Is there an elegant workaround if not?
A solution I found was to use a LEFT JOIN and filter in the SELECT expression. So say we have an table to_upsert identical to the destination table with all our potential upserts...
INSERT INTO to_upsert VALUES (3, "bobby" 100), (5, "newgal", 600);
UPSERT INTO my_first_table
SELECT to_upsert.id, to_upsert.name, to_upsert.status
FROM to_upsert
LEFT JOIN my_first_table ON to_upsert.id = my_first_table.id
WHERE my_first_table.status > to_upsert.status OR my_first_table.id IS NULL;
SELECT * FROM my_first_table;
+----+--------+--------+
| id | name | status |
+----+--------+--------+
| 3 | bobby | 100 |
| 1 | lee | 101 |
| 2 | shiv | 102 |
| 5 | newgal | 600 |
+----+--------+--------+
Thank you for watching this episode of watching me learn sql.

Select lowest 2 scores from average

I've got a result set with multiple scores per item. I need to select the 2 lowest scoring items per store. Each item is present multiple times in the sample, I need to base the order on the average. Example:
+------------+--------------+--------+
| Store | Item | Score |
+------------+--------------+--------+
| Amsterdam | Shirt_Black | 6 |
| Amsterdam | Shirt_Blue | 8 |
| Amsterdam | Shirt_White | 5 |
| Amsterdam | Pants_Black | 3 |
| Amsterdam | Pants_Black | 1 |
| Amsterdam | Socks_Blue | 8 |
| Amsterdam | Shirt_Black | 1 |
| Rotterdam | Shirt_Blue | 5 |
| Rotterdam | Shirt_White | 3 |
| Rotterdam | Pants_Black | 7 |
| Rotterdam | Socks_White | 6 |
+------------+--------------+--------+
Should become
+------------+-------------+-------+
| Store | Item | Score |
+------------+-------------+-------+
| Amsterdam | Pants_Black | 2 |
| Amsterdam | Shirt_Black | 3.5 | (Average is lower than lowest score)
| Rotterdam | Shirt_White | 3 |
| Rotterdam | Shirt_Blue | 5 |
+------------+-------------+-------+
I have tried Group by and a partition by. But it doesn't yet seem to work as intended. Hoping you guys can give me a nudge in the right direction.
Thanks!
The following query will work with the average calculation:
SELECT Store, Item, CalcAvg AS Score
FROM (
SELECT Store, Item, CalcAvg, ROW_NUMBER() OVER (PARTITION BY Store ORDER BY CalcAvg ASC) AS Rn
FROM (
SELECT Store, Item, CAST(AVG(CAST(Score AS DECIMAL(9,2))) AS DECIMAL(9, 2)) AS CalcAvg
FROM TestTable
GROUP BY Store, Item
) AS Q
)AS R
WHERE R.Rn <= 2;
Demo with sample data:
DECLARE #TestTable TABLE (Store VARCHAR (20), Item VARCHAR (20), Score INT);
INSERT INTO #TestTable (Store, Item, Score) VALUES
('Amsterdam', 'Shirt_Black', 6),
('Amsterdam', 'Shirt_Blue', 8),
('Amsterdam', 'Shirt_White', 5),
('Amsterdam', 'Pants_Black', 3),
('Amsterdam', 'Pants_Black', 1),
('Amsterdam', 'Socks_Blue', 8),
('Amsterdam', 'Shirt_Black', 1),
('Rotterdam', 'Shirt_Blue', 5),
('Rotterdam', 'Shirt_White', 3),
('Rotterdam', 'Pants_Black', 7),
('Rotterdam', 'Socks_White', 6);
SELECT Store, Item, CalcAvg AS Score
FROM (
SELECT Store, Item, CalcAvg, ROW_NUMBER() OVER (PARTITION BY Store ORDER BY CalcAvg ASC) AS Rn
FROM (
SELECT Store, Item, CAST(AVG(CAST(Score AS DECIMAL(9,2))) AS DECIMAL(9, 2)) AS CalcAvg
FROM #TestTable
GROUP BY Store, Item
) AS Q
)AS R
WHERE R.Rn <= 2;
Output:
Store Item Score
--------------------------------
Amsterdam Pants_Black 2.00
Amsterdam Shirt_Black 3.50
Rotterdam Shirt_White 3.00
Rotterdam Shirt_Blue 5.00

PIVOT with dynamic columns and JOINS 5+ tables

This is an extension of a question posted by me earlier which can be found here : Previous Question
Now i have 2 more tables that are also connected to ProfileFan tracking the Activities of the Fan(ProfileFan).
Table Fan
-----------------------
| FanId | Name | Info |
-----------------------
| 17111 | Fan1 | Info1|
-----------------------
| 17112 | Fan2 | Info2|
-----------------------
Table ProfileFan m:1 Fan FanId(FK)
-----------------------------------
| Id | LinkedInProfileId | FanId |
-----------------------------------
| 1111 | 1 | 17111 |
---------------------------------
| 1112 | 2 | 17111 |
----------------------------------
| 1113 | 1 | 17112 |
----------------------------------
| 1114 | 2 | 17112 |
----------------------------------
Table LinkedInProfile
--------------------------
| Id | Name | Client |
--------------------------
| 1 | Linked1 | Client1|
--------------------------
| 2 | Linked2 | Client1|
--------------------------
Table FanActivity m:1 ProfileFan via ProfileFanId (FK)
------------------------------------------------
| Id | Created | ProfileFanId | ActivityId |
-------------------------------------------------
| 1 | 2019-01-05 | 17111 | 1 |
-------------------------------------------------
| 2 | 2019-01-05 | 17111 | 2 |
-------------------------------------------------
| 3 | 2019-01-05 | 17112 | 3 |
-------------------------------------------------
| 4 | 2019-01-05 | 17112 | 4 |
-------------------------------------------------
Table Activity Id(PK)
--------------------
| Id | Name |
---------------------
| 1 | ConAccepted |
---------------------
| 2 | Message |
---------------------
| 3 | LikesContent |
----------------------
| 4 | JoinsGroup |
----------------------
Table DeliveryActions m:1 ProfileFan via ProfileFanId (FK)
------------------------------------------------------
| Id | LoggedAt | ProfileFanId | DeliveryActionId |
-------------------------------------------------------
| 1 | 2019-01-05 | 17111 | 1 |
------------------------------------------------------
| 2 | 2019-01-05 | 17111 | 2 |
------------------------------------------------------
| 3 | 2019-01-05 | 17112 | 3 |
------------------------------------------------------
| 4 | 2019-01-05 | 17112 | 4 |
------------------------------------------------------
Table DeliveryAction Id(PK)
------------------------
| Id | Name |
------------------------
| 1 | M1 |
------------------------
| 2 | M2 |
------------------------
| 3 | M3 |
------------------------
| 4 | M4 |
------------------------
The output of the query should look like this.
-------------------------------------------------------------------------------------------------------------------------------------------------------
| Name | Info | LinkedInProfile1| LinkedInProfile2 | Client |ConAccepted | Message | LikesContent | JoinsGroup | M1 | M2 | M3 | M4 |
---------------------------------------------------------------------------------------------------------------------------------------------------
| Name1 | Info1| Linked1(1111) | Linked2(1112) | Client1 | 2019-01-05 | 2019-01-06 | 2019-01-07 | 2019-01-08 | 2019-01-09 | 2019-01-05 |2019-01-06 | 2019-01-07 |
---------------------------------------------------------------------------------------------------------------------------------------------------
| Name2 | Info2| Linked1(1113) | Linked2(1114) | Client1 |2019-01-05 | 2019-01-06 | 2019-01-07 | 2019-01-08 | 2019-01-09 | 2019-01-05 |2019-01-05 | 2019-01-0 |
---------------------------------------------------------------------------------------------------------------------------------------------------
I need to insert the data from a query for one fan in a single row instead multiple for every row that gives me the joins.
So far as suggested by stackoverflow user i have come to solution to do it for only the LinkedInProfile by using PIVOT and dynamic query.
I Have also created a simulation here https://rextester.com/live/NYYK67222
I need to make the appropriate joins with the FanActivities and DeliveryActions Table On top of the joins so far and put the data in a single row per FAN!
So 1 FAN is under 2 LinkedInProfiles -> This creates 2 ProfileFansId. All the profileFans have many FanActivities and Many DeliveryActions.
I know that i need to use PIVOT with dynamic columns here. But its all new to me.
Since your desired result is different from your sample data on rexter, I just modified few data in FanActivities & DeliveryActions tables.
CREATE TABLE #Fan (FanId INT, Name VARCHAR(100), Info VARCHAR(100));
INSERT INTO #Fan
VALUES (17111, 'Fan1', 'Info1'), (17112, 'Fan2', 'Info2');
CREATE TABLE #LinkedInProfile (Id INT, Name VARCHAR(100), Client VARCHAR(100));
INSERT INTO #LinkedInProfile
VALUES (1, 'Linked1', 'Client1'), (2, 'Linked2', 'Client1');
CREATE TABLE #ProfileFan (Id INT, ProfileId INT, FanId INT);
INSERT INTO #ProfileFan
VALUES (1111, 1, 17111), (1112, 2, 17111), (1113, 1, 17112), (1114, 2, 17112);
CREATE TABLE #Activities (Id INT, Name VARCHAR(100));
INSERT INTO #Activities
VALUES (1, 'ConAccepted'), (2, 'Message'), (3, 'LikesContent'), (4, 'JoinsGroup');
CREATE TABLE #FanActivities (Id INT, Created VARCHAR(100), ProfileFanId INT
, ActivityId INT);
INSERT INTO #FanActivities
VALUES
(1, '2019-01-05', 1111, 1), (1, '2019-01-05', 1111, 2)
, (1, '2019-01-05', 1112, 3), (1, '2019-01-05', 1111, 4)
, (1, '2019-01-05', 1113, 1), (1, '2019-01-05', 1113, 2)
, (1, '2019-01-05', 1114, 3), (1, '2019-01-05', 1113, 4);
CREATE TABLE #DeliveryAction (Id INT, Name VARCHAR(100));
INSERT INTO #DeliveryAction
VALUES (1, 'M1'), (2, 'M2'), (3, 'M3'), (4, 'M4');
CREATE TABLE #DeliveryActions (Id INT, Created VARCHAR(100), ProfileFanId INT
, ActivityId INT);
INSERT INTO #DeliveryActions
VALUES
(1, '2019-01-05', 1111, 1), (1, '2019-01-05', 1111, 2)
, (1, '2019-01-05', 1112, 3), (1, '2019-01-05', 1111, 4)
, (1, '2019-01-05', 1113, 1), (1, '2019-01-05', 1113, 2)
, (1, '2019-01-05', 1114, 3), (1, '2019-01-05', 1113, 4);
And your query would be
;WITH CTE AS(
SELECT F.FanId,F.Name AS FANNAME,F.Info
,CONCAT(lip.Name,'(',pf.Id,')') AS LINKEDPROFILE
,CONCAT('#LinkedInProfile',pf.ProfileId) as LipId
, LIP.Client
,FACT.Name AS FANACTIVITY, FA.Created FANACTIVITYDATE
,DA.Name AS DELVACTIVITY, DAS.Created DELVACTIVITYDATE
FROM #Fan F
LEFT JOIN #ProfileFan pf ON f.FanId = pf.FanId
LEFT JOIN #LinkedInProfile lip ON pf.ProfileId = lip.Id
LEFT JOIN #FanActivities FA ON PF.Id = FA.ProfileFanId
LEFT JOIN #ActivitieS FACT ON FACT.Id = FA.ActivityId
LEFT JOIN #DeliveryActions DAS ON PF.Id = DAS.ProfileFanId
LEFT JOIN #DeliveryAction DA ON DAS.ActivityId = DA.ID
)
SELECT FANNAME, Info, Client
,[#LinkedInProfile1],[#LinkedInProfile2]
,[ConAccepted],[Message],[LikesContent],[JoinsGroup]
,[M1],[M2],[M3],[M4] FROM (
SELECT FANNAME, Info, Client,KEYS,VALUE
FROM CTE
CROSS APPLY (
VALUES (LipId, LINKEDPROFILE)
, (FANACTIVITY,FANACTIVITYDATE)
,(DELVACTIVITY,DELVACTIVITYDATE)
)AS TAB(KEYS,VALUE)
)AS A
PIVOT
(
MAX(VALUE) FOR KEYS IN ([#LinkedInProfile1],[#LinkedInProfile2]
,[ConAccepted],[Message],[LikesContent],[JoinsGroup]
,[M1],[M2],[M3],[M4])
)PV
And the result I got
+---------+-------+---------+-------------------+-------------------+-------------+------------+--------------+------------+------------+------------+------------+------------+
| FANNAME | Info | Client | #LinkedInProfile1 | #LinkedInProfile2 | ConAccepted | Message | LikesContent | JoinsGroup | M1 | M2 | M3 | M4 |
+---------+-------+---------+-------------------+-------------------+-------------+------------+--------------+------------+------------+------------+------------+------------+
| Fan1 | Info1 | Client1 | Linked1(1111) | Linked2(1112) | 2019-01-05 | 2019-01-05 | 2019-01-05 | 2019-01-05 | 2019-01-05 | 2019-01-05 | 2019-01-05 | 2019-01-05 |
| Fan2 | Info2 | Client1 | Linked1(1113) | Linked2(1114) | 2019-01-05 | 2019-01-05 | 2019-01-05 | 2019-01-05 | 2019-01-05 | 2019-01-05 | 2019-01-05 | 2019-01-05 |
+---------+-------+---------+-------------------+-------------------+-------------+------------+--------------+------------+------------+------------+------------+------------+
There's some discrepancies between the sample code in the post and your rextester sample (for example, DeliveryActions.ProfileFanId in your post appears to reference Fan.FanId , but in the rextester sample it references ProfileFan.Id). My answer is built off of your rextester sample.
My personal preference is to not do multiple pivots if I don't have to and, instead, use a cross tab query... it's just easier for me to look at and comprehend rather than having multiple layers of pivots.
You can see this in rextester here.
SELECT
f.Name
,f.Info
,LinkedInProfile1 = MAX(CASE WHEN pf.ProfileId = 1 THEN CONCAT(lip.Name,'(',pf.Id,')') END)
,LinkedInProfile2 = MAX(CASE WHEN pf.ProfileId = 2 THEN CONCAT(lip.Name,'(',pf.Id,')') END)
,lip.Client
,ConAccepted = MAX(CASE WHEN a.Name = 'ConAccepted' THEN fa.Created END)
,Message = MAX(CASE WHEN a.Name = 'Message ' THEN fa.Created END)
,LikesContent = MAX(CASE WHEN a.Name = 'LikesContent ' THEN fa.Created END)
,JoinsGroup = MAX(CASE WHEN a.Name = 'JoinsGroup ' THEN fa.Created END)
,M1 = MAX(CASE WHEN da.Name = 'M1' THEN das.Created END)
,M2 = MAX(CASE WHEN da.Name = 'M2 ' THEN das.Created END)
,M3 = MAX(CASE WHEN da.Name = 'M3 ' THEN das.Created END)
,M4 = MAX(CASE WHEN da.Name = 'M4 ' THEN das.Created END)
FROM fan f
JOIN dbo.ProfileFan pf
ON pf.FanId = f.FanId
JOIN dbo.LinkedInProfile lip
ON lip.Id = pf.ProfileId
JOIN dbo.FanActivities fa
ON fa.ProfileFanId = pf.Id
JOIN Activities a
ON a.Id = fa.ActivityId
JOIN dbo.DeliveryActions das
ON das.ProfileFanId = pf.Id
JOIN dbo.DeliveryAction da
ON da.Id = das.ActivityId
GROUP BY f.Name, f.info, lip.Client

SQL Query to count number of records must match total number of records

I have 2 tables as
Result Master
+------+-------------+
| QnID | Description |
+------+-------------+
| 1 | Qn1 |
| 2 | Qn2 |
| 3 | Qn3 |
| 4 | Qn4 |
| 5 | Qn5 |
+------+-------------+
Result Details
+----+------+--------+--------+
| ID | QnID | TCDesc | Result |
+----+------+--------+--------+
| 1 | 1 | TC1 | PASS |
| 2 | 1 | TC2 | FAIL |
| 3 | 1 | TC3 | PASS |
| 4 | 2 | TC1 | PASS |
| 5 | 3 | TC1 | PASS |
| 6 | 3 | TC1 | PASS |
| 7 | 3 | TC3 | PASS |
+----+------+--------+--------+
I need a query which will return following result:
+----+------+--------+
| ID | QnID | Result |
+----+------+--------+
| 1 | 2 | PASS |
| 2 | 3 | PASS |
| 3 | 4 | ERROR |
| 4 | 5 | ERROR |
+----+------+--------+
Conditions:
each question will have different number of testcase "ResultDetails", I need to select questions for which all the test case get passsed (number of entries for a particular question must be same as number of test cases passed for the same) or Error (ResultDetail doesn't have an entry for a question).
Can anyone please help me with a query, thank you.
You can get the desired results using a common table expression and conditional aggregation.
First, create and populate sample tables (Please save us this step in your future questions):
DECLARE #ResultMaster AS TABLE
(
QnID int,
Description char(3)
);
INSERT INTO #ResultMaster (QnID, Description) VALUES
(1, 'Qn1'),
(2, 'Qn2'),
(3, 'Qn3'),
(4, 'Qn4'),
(5, 'Qn5');
DECLARE #ResultDetails AS TABLE
(
ID int,
QnID int,
TCDesc char(3),
Result char(4)
);
INSERT INTO #ResultDetails VALUES
(1, 1, 'TC1', 'PASS'),
(2, 1, 'TC2', 'FAIL'),
(3, 1, 'TC3', 'PASS'),
(4, 2, 'TC1', 'PASS'),
(5, 3, 'TC1', 'PASS'),
(6, 3, 'TC1', 'PASS'),
(7, 3, 'TC3', 'PASS');
Then, use a common table expression to calculate the number of pass details and a simple count to get the number of total details:
WITH CTE AS
(
SELECT M.QnId,
COUNT(CASE WHEN Result = 'PASS' THEN 1 END) As CountPass,
COUNT(Result) As CountDetails
FROM #ResultMaster As M
LEFT JOIN #ResultDetails As D ON M.QnId = D.QnId
GROUP BY M.QnId
)
Then, select from that cte:
SELECT ROW_NUMBER() OVER(ORDER BY QnId) AS Id,
QnId,
CASE WHEN CountDetails = 0 THEN
'ERROR'
ELSE
'PASS'
END
FROM CTE
WHERE CountPass = CountDetails
Results:
+----+------+--------+
| ID | QnID | Result |
+----+------+--------+
| 1 | 2 | PASS |
| 2 | 3 | PASS |
| 3 | 4 | ERROR |
| 4 | 5 | ERROR |
+----+------+--------+
You can see a live demo on rextester.

PostgreSQL Current count of specific value

I need to achieve a view such as:
+------------+----------+--------------+----------------+------------------+
| Parent id | Expected | Parent Value | Distinct Value | Distinct Value 2 |
+------------+----------+--------------+----------------+------------------+
| 1 | 001.001 | 3 | 6/1/2017 | 5,000.00 |
| 1 | 001.002 | 3 | 9/1/2018 | 3,500.00 |
| 1 | 001.003 | 3 | 1/7/2018 | 9,000.00 |
| 2 | 002.001 | 7 | 9/1/2017 | 2,500.00 |
| 3 | 003.001 | 5 | 3/6/2017 | 1,200.00 |
| 3 | 003.002 | 5 | 16/8/2017 | 8,700.00 |
+------------+----------+--------------+----------------+------------------+
where I get distinct child objects that have same parents, but I cannot make the "Expected" column work. Those zeros don't really matter, I just need to get subindex like "1.1", "1.2" to work. I tried rank() function but it seems it doesnt really help.
Any help appreciated, thanks in advance.
My initial try looks like this:
SELECT DISTINCT
parent.parent_id,
rank() OVER ( order by parent_id ) as expected,
parent.parent_value,
ct.distinct_value,
ct.distinct_value_2
FROM parent
LEFT JOIN (crosstab (...) )
AS ct( ... )
ON ...
Use partition by parent_id in the window function and order by another_col to define the order in groups by parent_id.
with parent(parent_id, another_col) as (
values (1, 30), (1, 20), (1, 10), (2, 40), (3, 60), (3, 50)
)
select
parent_id,
another_col,
format('%s.%s', parent_id, row_number() over w) as expected
from parent
window w as (partition by parent_id order by another_col);
parent_id | another_col | expected
-----------+-------------+----------
1 | 10 | 1.1
1 | 20 | 1.2
1 | 30 | 1.3
2 | 40 | 2.1
3 | 50 | 3.1
3 | 60 | 3.2
(6 rows)