How to get value conditionally from another row in sub table - sql-server

Select * from LoanAccount main INNER JOIN LoanSubAccount sub
WHERE main.LoanAccountID = sub.LoanAccountID
AND sub.LoanStatus = 4
My objective is to retrieve rows with LoanStatus = 4 but replace the amount with records with LoanStatus = 2.
End result expected to be

WITH cte AS (
SELECT *,
ROW_NUMBER() OVER (PARTITION BY LoanAccountID, LoanStatus
ORDER BY LoanSubAccountID) rn
FROM LoanSubAccount
)
SELECT t1.LoanSubAccountID,
t1.LoanAccountID,
t1.LoanStatus,
t1.CommodityType,
t2.Amount
FROM cte t1
INNER JOIN cte t2
ON t1.rn = t2.rn AND
t1.LoanStatus > t2.LoanStatus
Rather than giving a verbose explanation, I would rather show a table representing what the above CTE would look like:
rn | LoanSubAccountID | LoanAccountID | LoanStatus | CommodityType | Amount
1 | 1 | 1 | 2 | 1 | 100
2 | 2 | 1 | 2 | 2 | 200
1 | 3 | 1 | 4 | 3 | 150
2 | 4 | 1 | 4 | 4 | 150
If I read your requirement correctly, you want to connect rows having the same row number from the two different loan statuses. The join query I gave above does this.

Related

How to update rows based on one field/column of snowflake tables

I want to update one table using another table on field "Id" such that it wont create duplicates
let say my first table is Table1 and second table is Table2 . I would like to update the row in Table1 from Table2 when the Id is matching
I am aware of using UNION function but this applies to entire columns where I only need to consider a single column.
https://docs.snowflake.com/en/sql-reference/operators-query.html#union-all
Example of my Tables
Table1
Id name number value
1 a 8 100
2 b 8 100
3 c 8 100
4 d 8 100
Table2
Id name number value
3 c 8 99
4 d 6 100
5 e 7 100
Expected output
Id name number value
1 a 8 100
2 b 8 100
3 c 8 99
4 d 6 100
5 e 7 100
Please note that in the output table row with Id 3,4 has be updated and new Id 5 is inserted. Can someone help me with the select query to get the desired output?
You can use MERGE command:
merge into table1 using table2 on table1.id = table2.id
when matched then
update set table1.name = table2.name, table1.number = table2.number, table1.value = table2.value
when not matched then
insert (Id,name,number,value) values (table2.id, table2.name, table2.number, table2.value);
select * from table1;
+----+------+--------+-------+
| ID | NAME | NUMBER | VALUE |
+----+------+--------+-------+
| 5 | e | 7 | 100 |
| 1 | a | 8 | 100 |
| 2 | b | 8 | 100 |
| 3 | c | 8 | 99 |
| 4 | d | 6 | 100 |
+----+------+--------+-------+
https://docs.snowflake.com/en/sql-reference/sql/merge.html
If you don't want to update the table, you may use IFNULL and full outer join:
select
IFNULL( t1.id, t2.id) id,
IFNULL( t2.name, t1.name ) name,
IFNULL( t2.number, t1.number ) number,
IFNULL( t2.value, t1.value ) value
from table1 t1
full join table2 t2
on t1.id = t2.id;
+----+------+--------+-------+
| ID | NAME | NUMBER | VALUE |
+----+------+--------+-------+
| 1 | a | 8 | 100 |
| 2 | b | 8 | 100 |
| 3 | c | 8 | 99 |
| 4 | d | 6 | 100 |
| 5 | e | 7 | 100 |
+----+------+--------+-------+

Getting duplicates with additional information

I've inherited a database and I'm having trouble constructing a working SQL query.
Suppose this is the data:
[Products]
| Id | DisplayId | Version | Company | Description |
|---- |----------- |---------- |-----------| ----------- |
| 1 | 12345 | 0 | 16 | Random |
| 2 | 12345 | 0 | 2 | Random 2 |
| 3 | AB123 | 0 | 1 | Random 3 |
| 4 | 12345 | 1 | 16 | Random 4 |
| 5 | 12345 | 1 | 2 | Random 5 |
| 6 | AB123 | 0 | 5 | Random 6 |
| 7 | 12345 | 2 | 16 | Random 7 |
| 8 | XX45 | 0 | 5 | Random 8 |
| 9 | XX45 | 0 | 7 | Random 9 |
| 10 | XX45 | 1 | 5 | Random 10 |
| 11 | XX45 | 1 | 7 | Random 11 |
[Companies]
| Id | Code |
|---- |-----------|
| 1 | 'ABC' |
| 2 | '456' |
| 5 | 'XYZ' |
| 7 | 'XYZ' |
| 16 | '456' |
The Versioncolumn is a version number. Higher numbers indicate more recent versions.
The Company column is a foreign key referencing the Companies table on the Id column.
There's another table called ProductData with a ProductId column referencing Products.Id.
Now I need to find duplicates based on the DisplayId and the corresponding Companies.Code. The ProductData table should be joined to show a title (ProductData.Title), and only the most recent ones should be included in the results. So the expected results are:
| Id | DisplayId | Version | Company | Description | ProductData.Title |
|---- |----------- |---------- |-----------|------------- |------------------ |
| 5 | 12345 | 1 | 2 | Random 2 | Title 2 |
| 7 | 12345 | 2 | 16 | Random 7 | Title 7 |
| 10 | XX45 | 1 | 5 | Random 10 | Title 10 |
| 11 | XX45 | 1 | 7 | Random 11 | Title 11 |
because XX45 has 2 "entries": one with Company 5 and one with Company 7, but both companies share the same code.
because 12345 has 2 "entries": one with Company 2 and one with Company 16, but both companies share the same code. Note that the most recent version of both differs (version 2 for company 16's entry and version 1 for company 2's entry)
ABC123 should not be included as its 2 entries have different company codes.
I'm eager to learn your insights...
Based on your sample data, you just need to JOIN the tables:
SELECT
p.Id, p.DisplayId, p.Version, p.Company, d.Title
FROM Products AS p
INNER JOIN Companies AS c ON p.Company = c.Id
INNER JOIN ProductData AS d ON d.ProductId = p.Id;
But if you want the latest one, you can use the ROW_NUMBER():
WITH CTE
AS
(
SELECT
p.Id, p.DisplayId, p.Version, p.Company, d.Title,
ROW_NUMBER() OVER(PARTITION BY p.DisplayId,p.Company ORDER BY p.Id DESC) AS RN
FROM Products AS p
INNER JOIN Companies AS c ON p.Company = c.Id
INNER JOIN ProductData AS d ON d.ProductId = p.Id
)
SELECT *
FROM CTE
WHERE RN = 1;
sample fiddle
| Id | DisplayId | Version | Company | Title |
|----|-----------|---------|---------|----------|
| 5 | 12345 | 1 | 2 | Title 5 |
| 7 | 12345 | 2 | 16 | Title 7 |
| 10 | XX45 | 1 | 5 | Title 10 |
| 11 | XX45 | 1 | 7 | Title 11 |
If i understood you correctly, you can use CTE to find all the duplicated rows from your table, then you can just use SELECT from CTE and even add more manipulations.
WITH CTE AS(
SELECT Id,DisplayId,Version,Company,Description,ProductData.Title
RN = ROW_NUMBER()OVER(PARTITION BY DisplayId, Company ORDER BY p.Id DESC)
FROM dbo.YourTable1
)
SELECT *
FROM CTE
Try this:
SELECT b.ID,displayid,version,company,productdata.title
FROM
(select A.ID,a.displayid,version,a.company,rn,a.code, COUNT(displayid) over (partition by displayid,code) cnt from
(select Prod.ID,displayid,version,company,Companies.code, Row_number() over (partition by displayid,company order by version desc) rn
from Prod inner join Companies on Prod.Company = Companies.id) a
where a.rn=1) b inner join productdata on b.id = productdata.id where cnt =2
You have to first get the current version and then you see how many times the DisplayID + Code show-up. Then based on that you can select only the ones that have a count greater than one. You can then INNER JOIN ProductData on the final query to get the Title.
WITH
MaxVersion AS --Get the current versions
(
SELECT
MAX(Version) AS Version,
DisplayID,
Company
FROM
#TmpProducts
GROUP BY
DisplayID,
Company
)
,CTE AS
(
SELECT
p.DisplayID,
c.Code,
COUNT(*) AS RowCounter
FROM
#TmpProducts p
INNER JOIN
#TmpCompanies c
ON
c.ID = p.Company
INNER JOIN
MaxVersion mv
ON
mv.DisplayID = p.DisplayID
AND mv.Version = p.Version
AND mv.Company = p.Company
GROUP BY
p.DisplayID,
c.Code
)
SELECT
p.*
FROM
#TmpProducts p
INNER JOIN
CTE c
ON
c.DisplayID = p.DisplayID
INNER JOIN
MaxVersion mv
ON
mv.DisplayID = p.DisplayID
AND mv.Company = p.Company
AND mv.Version = p.Version
WHERE
c.RowCounter > 1

SQL Server - How to join with max value from second table and apply specific condition

I have two tables:
Weeks
| WeekID | StartDate |
| 1 | 2016-12-25 |
| 2 | 2017-01-01 |
| 3 | 2017-01-08 |
and Settings
| ID | SettingVal | ApplyFrom |
| 1 | 10 | 2016-06-01 |
| 2 | 13 | 2017-01-01 |
| 3 | 5 | 2017-01-02 |
For each WeekID, I need to select SettingVal with MAX(ApplyFrom) existing, but also ApplyFrom <= DATEADD(day, 6, StartDate) from table Weeks, for example:
| WeekID | SettingVal |
| 1 | 10 |
| 2 | 5 |
| 3 | 5 |
When I write the following query:
SELECT t1.WeekID, t2.SettingVal
FROM Weeks t1
LEFT OUTER JOIN Settings t2 ON t2.ApplyFrom <= DATEADD(day, 6, t1.StartDate)
it joins one row from first table with multiple rows from second table. How do I join only with a row having MAX(ApplyFrom), and select the SettingVal column I need?
Option 1 - WITH TIES
Select Top 1 with ties
A.WeekID
,B.SettingVal
From Weeks A
Left Join Settings B
on B.ApplyFrom<=DateAdd(DAY,6,A.StartDate)
Order By Row_Number() over (Partition By A.WeekID Order by B.ApplyFrom Desc)
Option 2 - Cross Apply
Select A.WeekID
,B.SettingVal
From Weeks A
Cross Apply (
Select Top 1 SettingVal
From Settings
Where ApplyFrom<=DateAdd(DAY,6,A.StartDate)
Order By ApplyFrom Desc
) B
Both Return
WeekID SettingVal
1 10
2 5
3 5
You can try using a query like this
SELECT
t1.WeekID,
t2.SettingVal
FROM Weeks t1
LEFT OUTER JOIN
(
SELECT
t3.WeekID,
MAX(t2.ApplyFrom) ApplyFrom
FROM Weeks t3
LEFT OUTER JOIN Settings t2 ON t2.ApplyFrom BETWEEN t3.StartDate AND DATEADD(day, 6, t3.StartDate)
GROUP BY t3.WeekID
)T4
ON T4.WeekID=T1.WeekID
LEFT OUTER JOIN Settings T2 ON T4.ApplyFrom=T2.ApplyFrom

Get the last n occurences of each value of a group

Let's say I have a table named tableA having a data of
| col1 | col2 |
| 1 | 5 |
| 1 | 6 |
| 1 | 7 |
| 2 | 1 |
| 2 | 2 |
| 2 | 3 |
| 2 | 4 |
| 3 | 3 |
| 3 | 2 |
| 3 | 1 |
Then what I would like to get is the last 2 occurrences of each unique value of col1. The result would be
| col1 | col2 |
| 1 | 6 |
| 1 | 7 |
| 2 | 3 |
| 2 | 4 |
| 3 | 2 |
| 3 | 1 |
Is there a single query to get this result?
You can use ROW_NUMBER
WITH CTE AS(
SELECT *, rn = ROW_NUMBER() OVER(PARTITION BY col1 ORDER BY col2 DESC)
FROM tbl
)
SELECT
col1, col2
FROM CTE
WHERE rn <= 2
Just expanding on Felix's answer, assuming there's an ID column: http://www.sqlfiddle.com/#!3/04a5e/3/0
WITH CTE AS(
SELECT *, rn = ROW_NUMBER() OVER(PARTITION BY cola ORDER BY id DESC)
FROM ta
)
SELECT
cola, colb
FROM CTE
WHERE rn <= 2
order by id
Need to reorder by ID to keep the correct order, plus ordering in the row_number() by the ID because col2 isn't always incremental.

Combine Parent-Child Rows - TSQL

lI am trying to flatten/combine rows from a table with a parent-child hierarchy. I'm trying to identify the beginning and the end of each 'link' - so if a is linked to b, b is linked to c, and then c is linked to d, I want the output to link a to d.
I'm trying my best to avoid using a procedure with loops, so any advice would be much appreciated!
The original dataset and the required output is as follows:
personID | form | linkedform
---------|---------|---------
1 | a | b
1 | b | c
1 | c | d
1 | d | NULL
2 | e | f
2 | f | g
2 | g | NULL
2 | h | i
2 | i | NULL
3 | j | NULL
3 | k | l
3 | l | NULL
Desired output:
personID | form | linkedform
---------|---------|---------
1 | a | d
2 | e | g
2 | h | i
3 | j | NULL
3 | k | l
Each personID can have multiple links, and a link can be made of just one or multiple forms.
-- use a recursive cte to build the hierarchy
-- start with [linkedform] = null and work your way up
;WITH cte AS
(
SELECT *, [form] AS [root],
1 AS [Level]
FROM Table1
WHERE [linkedform] IS NULL
UNION ALL
SELECT t1.*,
[root],
[Level] + 1
FROM Table1 t1
JOIN cte ON cte.form = t1.linkedform
)
-- level 1 will be the last element, use row_number to get the first element
-- join the two together based on last and first level, that have the same personid and root ([linkedform] = null)
SELECT cte.personId,
cte2.form,
cte.form
FROM cte
JOIN ( SELECT *,
ROW_NUMBER() OVER (PARTITION BY personId, [root] ORDER BY Level DESC) Rn
FROM cte) cte2
ON cte2.Rn = cte.Level
AND cte2.personId = cte.personId
AND cte2.root = cte.root
WHERE cte.[Level] = 1
ORDER BY cte.personId, cte2.form

Resources