teradata rank event data based on group where group has events - teradata

I have a dataset where im trying to generate a sequence number in teradata
In the dataset below, an order (550) is subjected to a number of modifications in a shop (10 in total)
The final column ShopSequence is what I want to achieve where the when the order is moved to a different shop the ShopSequence number increments by 1
So this dataset reads as subjected to 10 modifcations across 4 shops
There are many such orders
Order, Shop, SequenceNumber, ShopSequence
550, AA, 1 1
550, AA, 2 1
550, AA, 3 1
550, BB, 4 2
550, BB, 5 2
550, BB, 6 2
550, BB, 7 2
650, CC, 8 3
650, CC, 9 3
650, CC, 10 3
I tried a number of OLAP configurations but unfortunately I couldn't get it to work
ROW_NUMBER() OVER (ORDER BY SequenceNumber asc, Shop asc) AS shop_row_number,
RANK() OVER (PARTITION BY Shop asc ORDER BY SequenceNumber ASC
Thank you for your time

Try a DENSE_RANK OVER (ORDER BY Shop asc) instead.
This works as long as there's no 2nd group of rows with the same shop value.
Edit:
According to your comment values might repeat, so this works for repeated groups, too, but needs nested OLAP-functions:
select ...
sum(flag) -- calculate the rank
over (partition by Order
order by SequenceNumber
rows unbounded preceding) as ShopSequence
from
(
select ...
-- flag the rows where a new group starts
-- 'min...' can be replaced by LAG in TD16.10+
case when min(Shop)
over (partition by Order
order by SequenceNumber
rows between 1 preceding and 1 preceding) = Shop
then 0
else 1
end as flag
) as dt

Related

Retain values only for certain section of data in teradata

Below is the link of my previous quetsion.
Retain values till there is a change in value in Teradata
It worked as suggested by one of the community members #Dnoeth. Can this retention be done only for certain section of data?
I.e, Retain data only for data where Dep is A or B . When Dep is C just use same value as input and no need to retain till certain value.
Data:
Cust_id Balance st_ts Dep
123 1000 27MAY2018 A
123 350 31MAY2018 A
256 2000 29MAY2018 B
345 1000 28APR2018 C
345 1200 26MAY2018 C
Output reqd:
Cust_id Balance st_ts Dep
123 1000 27MAY2018 A
123 1000 28MAY2018 A
123 1000 29MAY2018 A
123 1000 30MAY2018 A
123 350 31MAY2018 A
256 2000 29MAY2018 B
256 2000 30MAY2018 B
256 2000 31MAY2018 B
345 1000 28APR2018 C
345 1200 26MAY2018 C
Query used:
Wth cte
{
SELECT customer_id, bal, st_ts,
-- return the next row's date
Coalesce(Min(st_ts)
Over (PARTITION BY customer_id
ORDER BY st_ts
ROWS BETWEEN 1 Following AND 1 Following)
,Date '2018-06-01') AS next_Txn_dt
FROM BAL_DET;
}
SELECT customer_id, bal
,Last(pd) -- last day of the period
FROM cTE
-- make a period of the current and next row's date
-- and return one row per day
EXPAND ON PERIOD(ST_TS, next_Txn_dt) AS pd;
Thanks
Sandy
You can add a CASE to check for Dep = 'C':
WITH cte AS
( SELECT customer_id, bal, st_ts, dep,
-- return the next row's date
CASE
WHEN dep = 'C'
THEN st_ts +1 -- simply increase date
ELSE
Coalesce(Min(st_ts)
Over (PARTITION BY customer_id
ORDER BY st_ts
ROWS BETWEEN 1 Following AND 1 Following)
,DATE '2018-06-01')
END AS next_Txn_dt
FROM BAL_DET
)
SELECT customer_id, bal
,Last(pd) -- last day of the period
,dep
FROM cTE
-- make a period of the current and next row's date
-- and return one row per day
EXPAND ON PERIOD(ST_TS, next_Txn_dt) AS pd

Creating even ranges based on values in an oracle table

I have a big table which is 100k rows in size and the PRIMARY KEY is of the datatype NUMBER. The way data is populated in this column is using a random number generator.
So my question is, can there be a possibility to have a SQL query that can help me with getting partition the table evenly with the range of values. Eg: If my column value is like this:
1
2
3
4
5
6
7
8
9
10
And I would like this to be broken into three partitions, then I would expect an output like this:
Range 1 1-3
Range 2 4-7
Range 3 8-10
It sounds like you want the WIDTH_BUCKET() function. Find out more.
This query will give you the start and end range for a table of 1250 rows split into 20 buckets based on id:
with bkt as (
select id
, width_bucket(id, 1, 1251, 20) as id_bucket
from t23
)
select id_bucket
, min(id) as bkt_start
, max(id) as bkt_end
, count(*)
from bkt
group by id_bucket
order by 1
;
The two middle parameters specify min and max values; the last parameter specifies the number of buckets. The output is the rows between the minimum and maximum bows split as evenly as possible into the specified number of buckets. Be careful with the min and max parameters; I've found poorly chosen bounds can have an odd effect on the split.
This solution works without width_bucket function. While it is more verbose and certainly less efficient it will split the data as evenly as possible, even if some ID values are missing.
CREATE TABLE t AS
SELECT rownum AS id
FROM dual
CONNECT BY level <= 10;
WITH
data AS (
SELECT id, rownum as row_num
FROM t
),
total AS (
SELECT count(*) AS total_rows
FROM data
),
parts AS (
SELECT rownum as part_no, total.total_rows, total.total_rows / 3 as part_rows
FROM dual, total
CONNECT BY level <= 3
),
bounds AS (
SELECT parts.part_no,
parts.total_rows,
parts.part_rows,
COALESCE(LAG(data.row_num) OVER (ORDER BY parts.part_no) + 1, 1) AS start_row_num,
data.row_num AS end_row_num
FROM data
JOIN parts
ON data.row_num = ROUND(parts.part_no * parts.part_rows, 0)
)
SELECT bounds.part_no, d1.ID AS start_id, d2.ID AS end_id
FROM bounds
JOIN data d1
ON d1.row_num = bounds.start_row_num
JOIN data d2
ON d2.row_num = bounds.end_row_num
ORDER BY bounds.part_no;
PART_NO START_ID END_ID
---------- ---------- ----------
1 1 3
2 4 7
3 8 10

sql group by with count and add totals row

So my situation. Got a table with few thousand entries, I took one column and counted the number of the same entries.
SELECT mycolumn, COUNT(*)
FROM mytable
WHERE myid = 6867
GROUP BY mycolumn
ORDER BY COUNT(*) DESC
Outputs:
6 885
1 715
4 562
5 557
2 232
3 181
I get the desired results. Now all I need is to add a bottom row with a sum of all counted entries.
Typically you would do this in some reporting tool, like SSRS, not SQL.
To do it in SQL, add a UNION statement:
UNION ALL
SELECT 0, COUNT(*)
FROM mytable
WHERE myid = 6867;

Increase row number every time a flag changes

I have gone through a similar post in Stack overflow...
but my query is :
If my table generates a flag in run time execution,then how can I increase Grp_number(generate run time) every time my flag changes.
my Oracle query:
Select emp_id,
Case when MOD(rownum/3)=1 and rownum>1 then 'Y' else 'N' as flag
from Transaction_table
Desired o/p Data format:
emp_id Flag GRP_number
1 N 1
2 N 1
3 N 1
4 Y 2
5 N 2
6 N 2
7 Y 3
You cannot reference a column in another column in the same select list. You need to use sub query to avoid INVALID IDENTIFIER error.
Do it like -
WITH DATA AS(
SELECT emp_id,
CASE
WHEN MOD(rownum/3)=1
AND rownum >1
THEN 'Y'
ELSE 'N' AS flag
FROM Transaction_table
)
SELECT emp_id, flag, SUM(gap) over (PARTITION BY person
ORDER BY DAY) grp
FROM(
SELECT emp_id, flag,
CASE WHEN flag = lag(flag) over (PARTITION BY person
ORDER BY DAY)
THEN 0
ELSE 1
END gap
FROM DATA)

Group by not returning 0 value

My table contains pk_id,reviewer_id,rating.
There are 4 type of rating.
1-very good.
2-good.
3-bad.
4-very bad.
I want to calculate how much rating given by each reviewer.
Means:
If Akee having id 200 has given 2 very good,4 good,3 bad and zero very bad rating to different code.
I want result
count--- rate
2---------1
4---------2
3---------3
0---------4
My query is
SELECT COUNT(RATE),RATE
FROM CODE_REVIEW WHERE CODE_REVIEWER_ID= 200
GROUP BY RATE;
It is showing result
count--- rate
2---------1
4---------2
3---------3
I want to show the fourth row that is 4 rating zero.
How can it be done??
If Rate is not the primary key in another table then you need define your own list of rates so MySQL knows what the permutations of rate are:
SELECT Rates.Rate,
COUNT(Code_Review.Rate) AS CountOfRate
FROM ( SELECT 1 AS Rate UNION ALL
SELECT 2 AS Rate UNION ALL
SELECT 3 AS Rate UNION ALL
SELECT 4
) AS Rates
LEFT JOIN Code_Review
ON Code_Review.Rate = Rates.Rate
AND CODE_REVIEWER_ID = 200
GROUP BY Rates.Rate
Try this query:
SELECT coalesce(c.cnt, 0), r.rate
FROM (SELECT 1 AS rate UNION ALL SELECT 2
UNION ALL SELECT 3 UNION ALL SELECT 4) AS r
LEFT JOIN (SELECT COUNT(RATE),RATE
FROM CODE_REVIEW WHERE CODE_REVIEWER_ID= 200
GROUP BY RATE) AS c
ON r.rate = c.rate;
The first subquery creates a list of possible rates. You can avoid it if you have a table which defines all rates;
Second subquery is yours;
LEFT JOIN guarantees that all rates will be shown;
coalesce() is needed to convert NULL into 0.
Assuming that you do not have a separate table where the rates are defined.
SElECT * from (
SELECT distinct(m.rate), countrate from code_review m
LEFT JOIN
(SELECT COUNT(rate) as countrate,rate FROM code_review
WHERE code_reviewer_id=200 GROUP BY rate) t
ON m.rate=t.rate) a
You could do it somthing like this
SELECT
rates.RATE
, SUM(COUNT) COUNT
FROM
(
SELECT 1 RATE, 0 COUNT UNION ALL
SELECT 2 RATE, 0 COUNT UNION ALL
SELECT 3 RATE, 0 COUNT UNION ALL
SELECT 4 RATE, 0 COUNT
) Rates
LEFT JOIN
(
SELECT
RATE
, COUNT(RATE) COUNT
FROM
CODE_REVIEW
WHERE
CODE_REVIEWER_ID= 200
GROUP BY RATE
) Ratings200
ON Ratings200.RATE = Rates.RATE
If you can, you should push to try to get it in column format as it is simple as:
SELECT
SUM(rate = 1) AS 1,
SUM(rate = 2) AS 2,
SUM(rate = 3) AS 3,
SUM(rate = 4) AS 4
FROM
code_review
WHERE
code_reviewer_id = 200
But if you really need a row format, you could do:
SELECT
a.rate,
COUNT(b.rate) AS cnt
FROM
(
SELECT 1 AS rate UNION ALL
SELECT 2 AS rate UNION ALL
SELECT 3 AS rate UNION ALL
SELECT 4 AS rate
) a
LEFT JOIN
code_review b ON a.rate = b.rate AND code_reviewer_id = 200
GROUP BY
a.rate
SELECT
Rate,
totCount
FROM
(
Select
Rate,
count(Rate) as totCount
from
Code_Review
where
CODE_REVIEWER_ID = 200
group by
Rate
union
select 4, 0
union
select 3, 0
union
select 2, 0
union
select 1, 0
) AS T
group by
T.Rate

Resources