Query for the scenario - teradata

I have to implement a query where the scenario is something like below;
Col_1 Col_2 Col_3 Col_4 Col_5
A AB AC AD AE
B BC BD BE ?
C CD CE ? ?
D DE ? ? ?
E ? ? ? ?
Any help on the below is highly appreciated.
Thanks,
Amit

You need different rows for each column:
select
col_1
,col_1 || min(col_1) over (order by ... rows between 1 following and 1 following)
,col_1 || min(col_1) over (order by ... rows between 2 following and 2 following)
,col_1 || min(col_1) over (order by ... rows between 3 following and 3 following)
,col_1 || min(col_1) over (order by ... rows between 4 following and 4 following)

{
SELECT CHR(64+LEVEL) AS A,
DECODE(SIGN( 4 - LEVEL ) , -1, '?',CHR(64+LEVEL)||CHR(65 +LEVEL)) B,
DECODE(SIGN( 3 - LEVEL ) , -1, '?',CHR(64+LEVEL)||CHR(66 +LEVEL)) C,
DECODE(SIGN( 2 - LEVEL ) , -1, '?',CHR(64+LEVEL)||CHR(67 +LEVEL)) D,
DECODE(sign( 1 - level ) , -1, '?',chr(64+level)||chr(68 +level)) E
from dual connect by level <6;
}
this above is using oracle 10g, other db should have some other strategy.

Related

case statement inside nested case

I will make this question as more precise
I have this data
id product count
1 a 10
1 b 20
1 c 10
1 d 30
I want the result like
Since both product A and B has values i want to count them as one so the result should count(distinct A,C,D) that is 3
If any of product that is (A has values but B does not or vice versa ) then also the result has to be 3
in case both product A and B does not have value then the result should be 2
how to achieve this by using a case statement in oracle plsql
I'm not sure how you define either count of a or either count of b not both, but if you defined it explicitly then you can try this one:
with t as (
select 1 as id, 'a' as product from dual
union all
select 1 as id, 'b' as product from dual
union all
select 1 as id, 'c' as product from dual
union all
select 1 as id, 'd' as product from dual
) select id,
product,
count( case when product in ('c', 'd', 'a') then 1 end ) --change 'a' to 'b' to get the the result for 'b'
from t
group by id, product;

Case statement based on partition of data

I'm trying to categorize my data based on the existence of text within a variable for each id that I have. For example:
ID Groupname
1 A
1 B
1 F
2 D
2 B
2 C
The result I want would be
ID Groupname Category
1 A AF
1 B AF
1 F AF
2 D D
2 B D
2 C D
I want to do something like the following but I cannot get it to work
Select * ,
CASE WHEN A,F in groupname (partition by id) THEN AF
WHEN D in groupname (partition by id) THEN D
....
ELSE null END
FROM table
Answers in postgres/redshift would greatly be appreciated!
EDIT:
I fixed the problem with F that JNevill brought up.
EDIT2:
One user suggested a simple solution that works if you only need 1 combination. E.g. If i see F or A then it's AF. But if I need to see A and F it will not work.
Here is the simple solution:
WHEN sum(CASE WHEN combo in (20,28,19,27) then 1 end) over (partition by log_id)=1 then 1000
If i want combinations I have to do the following which creates very long code. Is there an easier way to do this?
WHEN sum(CASE WHEN combo in (20,28,19,27) then 1 end) over (partition by log_id)=1
and sum(CASE WHEN groupid in (1048,598,428) then 1 end) over (partition by log_id)=1
then 1000
you're right about case with partitioning, it just requires conditional aggregate
select
*
,case
when sum(case when groupname in ('A','F') then 1 end) over (partition by id)=1 then 'AF'
when sum(case when groupname in ('D') then 1 end) over (partition by id)=1 then 'D'
end as category
from t
one note is that if you want only the pair of A and F to make AF category it's not possible with this syntax... count(distinct case...) over () returns an error and requires more complex query, but from what I see you don't need it since F is not in your sample data

How to calculate a row value based on the previous row value in the same column

I have the following data set:
DATE CODE RANK PARTITION
? ABS 0 1
12/04/2014 RET 1 1
20/04/2014 RET 2 1
01/05/2014 ABS 2 1
13/05/2014 RET 2 1
01/06/2015 ABS 2 1
09/10/2015 RETk 2 1
? ABS 0 2
02/04/2015 RET 1 2
03/04/2015 RET 2 2
04/04/2015 ABS 2 2
05/04/2015 STT 3 2
06/04/2015 RETk 4 2
07/04/2015 RETk 4 2
RANK is the column I want to calculate in my SQL given the columns DATE, CODE AND the previous value of the same column. It's initialized here to 0.
The logic I want to implement is as follows:
If RANK-1 (previous row) IS NULL AND CODE = ABS THEN RANK = 0
If RANK-1 (previous row) IS NULL AND CODE <> ABS THEN RANK <- (RANK-1) + 1
If RANK-1 = 0 or 1 AND CODE = RET THEN RANK <- (RANK-1) + 1
If RANK-1 = 2 AND CODE = STT THEN RANK <- (RANK-1) + 1
If RANK-1 = 3 AND CODE = RETk THEN RANK <- (RANK-1) + 1
If CODE = ABS THEN RANK <- (RANK-1) (previous row)
Else 0
The Teradata release I am using is R14. The calculation is done on a partition basis as shown in the example above. I have added some more constraints in the model to make it clearer. In this example, if the current code is RET, I do not increase the rank until the previous one is 0 or 1. Similarly, If my current code is RETk, I do not increase the rank until the previous one is equal to 3, otherwise, I do not change the rank. I repeat the same process in the following partition and so on ...
I cannot figure out how to update the current column value given the previous one... I tried many logic implementation with OLAP functions without success.
Can anyone give me a hint?
Thank you very much for your help
You can always use a recursive query for tasks like this. But performance will be bad unless the number of rows per group is low.
First you need a way to advance to the next row, as the next row's date can't be calculated based on the current row's date you must materialize the data and add a ROW_NUMBER:
CREATE TABLE tab(dt DATE, CODE VARCHAR(10), rnk INT, part INT);
INSERT INTO tab( NULL,'ABS' ,0 , 1);
INSERT INTO tab(DATE'2014-04-12','RET' ,1 , 1);
INSERT INTO tab(DATE'2014-04-20','RET' ,2 , 1);
INSERT INTO tab(DATE'2014-05-01','ABS' ,2 , 1);
INSERT INTO tab(DATE'2014-05-13','RET' ,2 , 1);
INSERT INTO tab(DATE'2014-06-01','ABS' ,2 , 1);
INSERT INTO tab(DATE'2014-10-09','RETk',2 , 1);
INSERT INTO tab( NULL,'ABS' ,0 , 2);
INSERT INTO tab(DATE'2015-04-02','RET' ,1 , 2);
INSERT INTO tab(DATE'2015-04-03','RET' ,2 , 2);
INSERT INTO tab(DATE'2015-04-04','ABS' ,2 , 2);
INSERT INTO tab(DATE'2015-04-05','STT' ,3 , 2);
INSERT INTO tab(DATE'2015-04-06','RETk',4 , 2);
INSERT INTO tab(DATE'2015-04-07','RETk',4 , 2);
CREATE VOLATILE TABLE vt AS
(
SELECT dt, code, part
-- used to find the next row
,ROW_NUMBER() OVER (PARTITION BY part ORDER BY dt) AS rn
FROM tab
) WITH DATA
PRIMARY INDEX(part, rn)
ON COMMIT PRESERVE ROWS
;
And now it's just applying your logic using CASE row after row:
WITH RECURSIVE cte (dt, code, rnk, part, rn) AS
(
SELECT
dt
,code
,CASE WHEN code = 'ABS' THEN 0 ELSE 1 END
,part
,rn
FROM vt
WHERE rn = 1
UNION ALL
SELECT
vt.dt
,vt.code
,CASE
WHEN cte.rnk IN (0,1) AND vt.CODE = 'RET' THEN cte.rnk + 1
WHEN cte.rnk = 2 AND vt.CODE = 'STT' THEN cte.rnk + 1
WHEN cte.rnk = 3 AND vt.CODE = 'RETk' THEN cte.rnk + 1
WHEN vt.CODE = 'ABS' THEN cte.rnk
ELSE cte.rnk
END
,vt.part
,vt.rn
FROM vt JOIN cte
ON vt.part =cte.part
AND vt.rn =cte.rn + 1
)
SELECT *
FROM cte
ORDER BY part, dt;
But I think your logic is not actually like this (based on the previous rows exact RANK value), you're just stuck in procedural thinking :-)
You might be able to do what you want using OLAP-functions only...
Something along the lines of:
create table table1
(
datecol date,
code varchar(10),
rankcol integer
);
--insert into table1 select '2014/05/13', 'RETj', 0;
select
case
when s1.code='ABS' and s2.rankcol = 1 then 1
when s1.code='RET' and s2.rankcol = 0 then 1
when s1.code='RET' and s2.rankcol = 1 then 2
else 0
end RET_res,
s1.*, s2.*
from
(select rankcol, code, row_number() OVER (order by datecol) var1 from table1) s1,
(select rankcol, code, row_number() OVER (order by datecol) var1 from table1) s2
where s1.var1=s2.var1-1
order by s1.var1
;

How can I show the row_number alphabetically

SELECT Row_number()
OVER (
ORDER BY setsid) AS Set_No,
setsid,
testid
FROM examsets
WHERE testid = 4
Result is :-
Set_No
-------------------
1
2
3
4
5
6
7
I want result As:-
Set_No
-------------------
a
b
c
d
e
f
g
How can I do this?
You can use this. It will not handle cases where you have more than 26 rows returned.
char(96+row_number() over(order by setsid))
Going from AA to ZZ will give you 676 different values.
select char(((T.N / 26) % 26) + 65) + char((T.N % 26) + 65) as Set_No
from (
select row_number() over(order by setsid) - 1 as N
from YourTable
) as T
SQL Fiddle
SELECT
char(ascii(row_number() over (order by setsid))+48) as Set_No
setsid,
testid
FROM
examsets
WHERE
testid = 4

Get the most recent record for each user where value is 'K', action id is null or its state is 1

I have the following tables in SQL Server:
user_id, value, date, action_id
----------------------------------
1 A 1/3/2012 null
1 K 1/4/2012 null
1 B 1/5/2012 null
2 X 1/3/2012 null
2 K 1/4/2012 1
3 K 1/3/2012 null
3 L 1/4/2012 2
3 K 1/5/2012 3
4 K 1/3/2012 null
action_id, state
----------------------------------
1 0
2 1
3 1
4 0
5 1
I need to return the most recent record for each user where the value is 'K', the action id is either null or its state is set to 1. Here's the result set I want:
user_id, value, date, action_id
----------------------------------
3 K 1/5/2012 3
4 K 1/3/2012 null
For user_id 1, the most recent value is B and its action id is null, so I consider this the most recent record, but it's value is not K.
For user_id 2, the most recent value is K, but action id 1 has state 0, so I fallback to X, but X is not K.
user_id 3 and 4 are straightforward.
I'm interested in Linq to SQL query in ASP.NET, but for now T-SQL is fine too.
The SQL query would be :
Select Top 1 T1.* from Table1 T1
LEFT JOIN Table2 T2
ON T1.action_id = T2.action_id
Where T1.Value = 'K' AND (T1.action_id is null or T2.state = 1)
Order by T1.date desc
LINQ Query :
var result = context.Table1.Where(T1=> T1.Value == "K"
&& (T1.action_id == null ||
context.Table2
.Where(T2=>T2.State == 1)
.Select(T2 => T2.action_id).Contains(T1.action_id)))
.OrderByDescending(T => T.date)
.FirstOrDefault();
Good Luck !!
This query will return desired result set:
SELECT
*
FROM
(
SELECT
user_id
,value
,date
,action_id
,ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY date DESC) RowNum
FROM
testtable
WHERE
value = 'K'
) testtable
WHERE
RowNum = 1
You can also try following approach if user_id and date combination is unique
Make sure to get the order of predicates in the join to be able to use indexes:
SELECT
testtable.*
FROM
(
SELECT
user_id
,MAX(date) LastDate
FROM
testtable
WHERE
value = 'K'
GROUP BY
user_id
) tblLastValue
INNER JOIN
testtable
ON
testtable.user_id = tblLastValue.user_id
AND
testtable.date = tblLastValue.LastDate
This would select the top entries for all users as described in your specification, as opposed to TOP 1 which just selects the most recent entry in the database. I'm assuming here that your tables are named users and actions:
WITH usersactions as
(SELECT
u.user_id,
u.value,
u.date,
u.action_id,
ROW NUMBER() OVER (PARTITION BY u.user_id ORDER BY u.date DESC, u.action_id DESC) as row
FROM users u
LEFT OUTER JOIN actions a ON u.action_id = a.action_id
WHERE
u.value = 'K' AND
(u.action_id IS NULL OR a.state = 1)
)
SELECT * FROM usersactions WHERE row = 1
Or if you don't want to use a CTE:
SELECT * FROM
(SELECT
u.user_id,
u.value,
u.date,
u.action_id,
ROW NUMBER() OVER (PARTITION BY u.user_id ORDER BY u.date DESC, u.action_id DESC) as row
FROM users u
LEFT OUTER JOIN actions a ON u.action_id = a.action_id
WHERE
u.value = 'K' AND
(u.action_id IS NULL OR a.state = 1)
) useractions
WHERE row = 1

Resources