Teradata SQL - How to transpose data in one row - sql

I have a table as below:
I currently have a query that selects records where SEQ=450 and RESULT='LT' OR SEQ=650 and RESULT='LT'. And for a particular ID, if there are both Sequences with 450 and 650 with RESULT='LT' like in this case as shown, I only keep the row with SEQ=450 and RESULT='LT'. However, what I want as a final output is also the SEQ and CODE values from the row above the 450 SEQ. like below
and if only 650 exists for an ID then,
Obviously in this case I would only choose seq=450.
The current query I have is
CREATE MULTISET VOLATILE TABLE DOM AS (
WITH cte AS (
SELECT DISTINCT ID
FROM MASTER
WHERE SEQ = 450
)
SELECT DISTINCT SCAN.ID, SCAN.SEQ, SCAN.CODE, SCAN.RESULT FROM MASTER
WHERE (MASTER.SEQ = 450 OR (MASTER.SEQ = 650 AND NOT EXISTS (
SELECT 1 FROM cte WHERE cte.ID = MASTER.ID AND cte.ID = MASTER.ID
))) AND MASTER.RESULT ='LT'
) WITH DATA PRIMARY INDEX (ID, SEQ) ON COMMIT PRESERVE ROWS;
Which gives me the output for this particular ID as:
How can I modify the query to also get the
other columns? Note: The SEQ_BEFORE will not always be 300 or 600, so I cannot just use that seq no. as a reference in the query.

This is how I understand the task: You want to get the data for seq 450 and 650 along with their predecessor values (seq 300 and 600 in your example) Then per ID you only want to select the row with the lesser ID of the two, so if you find only one of the two sequences 450 and 650, you show it, if there exist both, you only show 450.
Use LAG to get the predecessor's values. Use MIN OVER to get the lesser seq per ID.
select *
from
(
select
id, seq, result, code,
lag(code) over (partition by id order by seq) as code_before,
lag(seq) over (partition by id order by seq) as seq_before
from mytable
) with_values_before
where seq in (450, 650)
qualify seq = min(seq) over (partition by id)
order by id;

Related

Select specific data from data only if certain fields can be grouped by

I have the following data:
ID Date Num ClientID Dest
--------------------------------------------------------
123 04/29/2021 -2222 H1234 -1
123 04/29/2021 1 H1234 3
345 04/29/2021 -2222 H3456 -1
345 04/29/2021 1 H8888 .1
BTW: this does not include all the fields, just what I'm currently using for my query.
For every ID in the above table I'll always have 2 records. There are 2 scenarios that can take place:
As for ID = 123, the ClientID is the same
As for ID = 345, the ClientID is different
I'm trying to return the following data:
ID Date Num ClientID Dest
123 04/29/2021 1 H1234 3
The reason I'm returning only this row is because:
I only want 1 row per ID, where the ClientID is the same for both rows
Only need the record that does not have -2222, where the CLientID is the same for both rows
If the ClientID is different for the same ID (ex: 345), then completely skip these records.
Now the numbers for DEST can vary, so we can't always rely that one will be -1 and the other will be positive, however the NUM field will always have -2222 and 1 for the 2nd row (which is the row that I'd want to be returned)
I'm not sure how best to do this, I guess I thought about the alternative of just creating a CTE, and then counting the ClientID and if Count = 2 then select the data. The problem I find is with DEST field, I know that I can do Max(NUM) but since DEST field can very I wouldn't know how to select it.
Here is what I tried:
WITH Ranking AS (
SELECT Rank() OVER (PARTITION BY c.ID,c.date1,c.ClientID ORDER BY num
asc)x, c.*
FROM cte c
)
SELECT * FROM Ranking WHERE x = 2
I'm not if this is a good apprach, I guess it does the job but any thoughts?
One solution is to use the ever-useful window functions with row_number to select which pair to use and lead to check if both ClientIds are the same. This would work regardless if the specific values you have should change, plus is more performant than hitting the table twice:
select id, date, num, clientid, Dest from (
select *,
Row_Number() over(partition by id order by num) rn,
case when Lead(clientid) over(partition by id order by num)=clientid then 1 else 0 end same
from t
)t
where rn=1 and same=1
Just select all rows where Num isn't -2222 and the ID is in a subquery grouped by id and having only one distinct client id.
SELECT *
FROM tbl
WHERE ID IN (SELECT ID FROM tbl GROUP BY ID HAVING COUNT(DISTINCT ClientId) = 1)
AND Num != -2222

Can I use a CTE data inside another CTE by joining both of them (Oracle SQL)

Requirement
I want to get the first four hundred GROUP_ID's from a table(greater than input GROUP_ID), and in the same table against each GROUP_ID, there are two LOG_ID's out of which I want the lowest one. Once I get the lowest LOG_ID, I will use that LOG_ID to get the data from another table where it is a foreign key.
APPROACH I USED
First I have formed a subset of top 400 GROUP_ID's which are greater than input GROUP_ID's
Then I used all the GROUP_IDs in my second subset where I will get the lowest LOG_ID against each GROUP_ID.
And finally, when I have the lowest LOG_ID, I used it to get the details from another table.
QUERY USED
WITH INIT AS (
SELECT GROUP_ID
FROM PV_ADAPTER_LOG
WHERE GROUP_ID > 2004141441192825
AND ADAPTER_ID IN ('2568','2602')
ORDER BY GROUP_ID
FETCH FIRST 400 ROWS ONLY
)
,INIT2 AS (
SELECT MIN(L.LOG_ID) AS LOG_ID
FROM PV_ADAPTER_LOG L
JOIN INIT ON INIT.GROUP_ID =L.GROUP_ID
GROUP BY L.GROUP_ID
)
SELECT A.LOG_ID,A.OPER_SEQ AS CALL_SEQUENCE,A.GROUP_ID ,B.INTERFACE_ID,A.INSTRUCTION_NAME, B.ADAPTER_DETAIL AS XML_CONTENT,B.SEQ AS XML_SEQUENCE
FROM INIT2
JOIN PV_ADAPTER_LOG A ON A.LOG_ID=INIT2.LOG_ID
JOIN PV_ADAPTER_LOG_DETAIL B ON B.LOG_ID=A.LOG_ID
Is my approach right or is there any other way to achieve this.
I think this is what you're looking for:
Use row_number ordered by group to find the first 400 rows
Use row_number partitioned by group and ordered by log to find the first log per group
Which is:
WITH INIT AS (
SELECT P.*,
ROW_NUMBER () OVER (
ORDER BY GROUP_ID
) RN,
ROW_NUMBER () OVER (
PARTITION BY GROUP_ID
ORDER BY LOG_ID
) MN
FROM PV_ADAPTER_LOG p
WHERE GROUP_ID > 2004141441192825
AND ADAPTER_ID IN ('2568','2602')
)
SELECT * FROM INIT
WHERE RN <= 400
AND MN = 1
You can use the analytical function to get the first 400 groups and then record with min log_id per group in a single query as follows:
SELECT GROUP_ID, LOG_ID FROM
(SELECT P.GROUP_ID, P.LOG_ID,
ROW_NUMBER() OVER (ORDER BY GROUP_ID) AS RNGRP,
ROW_NUMBER() OVER (PARTITION BY GROUP_ID ORDER BY LOG_ID) AS RNLOG
FROM PV_ADAPTER_LOG
WHERE GROUP_ID > 2004141441192825
AND ADAPTER_ID IN ('2568','2602'))
WHERE RNGRP <= 400 AND RNLOG = 1;
You can then use it wherever you want to use it. (In CTE or In Inner view)

Second method for getting output using SQL query in ORACLE

I have a table which has data as:
My expected output is:
I got my expected output as using rownum:
SELECT ID,PRICE FROM OT.TEST1 WHERE ROWNUM<3;
It's working finesince i have inserted the data serially as the output is coming with rownum ,but what,if the data were inserted as random below,my rownum will not work.Is there any new method?
ID PRice
3 300
3 600
8 600
2 600
You could use ROW_NUMBER() here:
WITH cte AS (
SELECT t.*, ROW_NUMBER() OVER (ORDER BY PRICE, ID) rn
FROM OT.TEST1 t
)
SELECT ID, PRICE
FROM cte
WHERE rn <= 2;
Here we are assigning a row number over the entire table ordered first by price ascending, and then by ID. Since three records are tied for a price of 600, the record with the lowest ID would happen to be returned here.

Using a CTE in OVER(PARTITION BY)

I'm trying to calculate volume from 3 columns in a table and return only unique volumes. We have many rows with the same Width, Height, and Length and so naturally my volume calculation will have duplicate return values for Volume. I am under the impression that, in order to accomplish this, I must use OVER, PARTITION and a CTE as aliases are not allowed to be referenced in OVER
WITH
cteVolume (Id, Volume)
AS
(
SELECT Id, Width * Height * [Length] AS Volume FROM PackageMaterialDimensions
)
SELECT *
INTO #volumeTempTable
FROM (
SELECT pp.ID, (pp.Width * pp.Height * pp.[Length]) AS Volume,
ROW_NUMBER() OVER(PARTITION BY cte.Volume ORDER BY pp.ID DESC) rn
FROM PlanPricing pp
INNER JOIN cteVolume cte ON pp.ID = cte.Id
) a
WHERE rn = 1
SELECT * FROM #volumeTempTable
ORDER BY Volume DESC
DROP TABLE #volumeTempTable
Note, the reason for the temp tables is because I plan on doing some extra work with this data. I also am currently debugging so I am using these tables to output to the data window
Here is what is wrong with this query
- It is still returning duplicates
- It is only returning one volume for every row
- It is only returning about 75 rows when there are 71000 rows in the table
How can I modify this query to essentially do the following
- Calculate volume for EVERY row in the table
- SELECT rows with unique volume calculations. (I do not want to see the same volume twice in my result set)
Edit - providing data as requested
Current data set Ignore the extra columns
What I would like is
ID | Volume
193 | 280
286 | 350
274 | 550
241 | 720
Basically, I want to calculate volume for every row, then I would like to somehow group by volume in order to cut down duplicates and select the first row from each group
Does this do what you want?
WITH cteVolume (Id, Volume) AS (
SELECT Id, Width * Height * [Length] AS Volume
FROM PackageMaterialDimensions
)
SELECT DISTINCT volume
FROM CTE ;
If you want one id per volume:
WITH cteVolume (Id, Volume) AS (
SELECT Id, Width * Height * [Length] AS Volume
FROM PackageMaterialDimensions
)
SELECT volume, MIN(Id) as Id
FROM CTE
GROUP BY volume;
Perhaps your issue is coming from partitioning cte.volume from the PackageMaterialDimensions table, but you're also selecting pp.volume from the PlanPricing table?
Not able to confirm without more information on your data set and tables.
As far as I can see you can't use windows functions inside the recursive part of the CTE. You have to sum them manually, inside the CTE part.
So, instead of
ROW_NUMBER() OVER(PARTITION BY cte.Volume ORDER BY pp.ID DESC) rn
Just write
1 as rn
in the first part, and
rn+1 as rn
in the second part.

How can I find the record with the max value for a group?

I am trying to write a query for a large dataset with many joins and having trouble accomplishing a particular piece without some sort of subquery, which I am trying to avoid.
For an example table with columns ID, Size, Item there may be multiple records with the same ID. I want to return the record per ID which has the largest Size.
ID Size Item
1 5 a
1 10 b
2 3 c
2 6 d
2 11 e
3 2 f
Expected result
ID Size Item
1 10 b
2 11 e
3 2 f
I've tried various group and having approaches without success.
Using a subquery I can do it like this but for a large dataset I'd prefer not to do it this way
select id, size, item
from test
where size = (select max(size) from test t2 where id = test.id)
Any suggestions?
This should satisfy your requirements: For each id, return only the row with the largest size
SELECT test.id, test.size, test.item
FROM test
INNER JOIN (
SELECT id, MAX(size) AS size
FROM test
GROUP BY id
) max_size ON max_size.id = test.id AND max_size.size = test.size
WITH T AS ( SELECT * ,
ROW_NUMBER() OVER ( PARTITION BY ID
ORDER BY Size DESC ) AS RN
FROM YourTable
)
SELECT ID ,
Size ,
Item
FROM T
WHERE RN = 1
SELECT id, item, MAX(size)
FROM Test
GROUP BY id, item
Assuming item is the same for every occurrence of that id.
select id, max(size), item
from test
group by id, item
Edit: Ah, the data you just added changes this and my above query no longer applies.
You can use this query(I mean your query) but it's necessary to create composite index (id, size)