I am working on a history handling issue. I am writing a query to update the wrong entries for the start_date. The data in the table is as below:
Subs_is subs_cd number start_dt end_dt
ABC 100 7854 10/8/2015 3/9/2015
ABC 100 58742 10/9/2015 20/09/2015
ABC 100 1278 23/09/2015 30/09/2015
ABC 100 4785 15/10/2015 25/10/2015
I want the start_date to be previous row end_date when the number changes.
can anyone please help me with this.
regards,
Amit
Seems to be a simple LAG (which is not implemented in Teradata, but easy to rewrite):
-- lag(start_date) -- not implemented
-- over (partition by Subs_is, subs_cd
-- order by start_dt
-- previous row's value
max(start_dt)
over (partition by Subs_is, subs_cd
order by start_dt
rows between 1 preceding and 1 preceding)
Related
Good Morning,
I am looking to add 'Previous Year Sales' to the same week into a Teradata pull, but I am failing on the coding. The Baseline I would code to get the sales of that week/year would be:
SELECT a."WEEK_NBR" AS "YearWeek"
, SUM(a."Sales") AS "CurrentYearSales"
FROM "SALESTABLE" AS a
Which would result in:
YearWeek CurrentYearSales
201901 $7,499
201902 $2,300
201903 $6,360
...
202001 $4,500
202002 $9,000
202003 $8,500
I want to be able to have the Prior year's sales same week on the same line, if the prior year is there in the data table. That way the finished table would look like:
YearWeek CurrentYearSales PriorYearSales
201901 $7,499 NULL
201902 $2,300 NULL
201903 $6,360 NULL
...
202001 $4,500 $7,499
202002 $9,000 $2,300
202003 $8,500 $6,360
When I search, all I can find is how to do this with the current week's data, but is this possible with all records in the table?
Edit: As you SUMmed the data you need to aggregate before the join.
You need a self join, assuming WEEK_NBR is numeric:
with cte as
(
SELECT a."WEEK_NBR" AS "YearWeek"
, SUM(a."Sales") AS "CurrentYearSales"
FROM "SALESTABLE" AS a
group by 1
)
select ...
from cte as t1
left join cte as t2
on t2.YearWeek = t1.Ye arWeek- 100
As #Andrew noted, this will also work for a string because Teradata will do an automatic typecast to a float when you add a number to a string or compare numeric and string (I would prefer writing an explicit type cast though)
The PROD_AMT I'd like to get is when ACCT_NBR, PROD_NBR And PROD_AMT are the same, I only need one PROD_AMT which is 100 (from distinct), and when ACCT_NBR are the same but PROD_NBR are different, then the PROD_AMT I need is 90 (30+60)
SELECT ACCT_NBR
,COUNT(DISTINCT CASE WHEN PROD_NBR = 1 THEN SUM(DISTINCT PROD_AMT)
WHEN PROD_NBR > 1 THEN SUM(PROD_AMT)
END) AS AMT
FROM TABLE
ACCT_NBR PROD_NBR PROD_AMT
3007 001 30
3007 002 60
1000 003 100
1000 003 100
There's probably a few ways to solve this. Using a subquery to determine which records should be summed vs which ones should be distinct, you could use:
SELECT
acct_nbr,
CASE WHEN sumflag = 'X' THEN SUM(prod_amt) ELSE MAX(prod_amt) END as amt
FROM
(
SELECT
acct_nbr,
prod_nbr,
prod_amt,
CASE WHEN COUNT(*) OVER (PARTITION BY Acct_nbr, prod_nbr, prod_amt) = 1 THEN 'X' ELSE NULL END AS sumflag
FROM
table
)t1
GROUP BY acct_nbr, sumflag
I'm just using MAX() here since it doesn't matter... all the values that will be aggregated with max() we know are duplicates, so it's a wash.
You could get similar results with a UNION query where one query would do the summing in the event that the records are distinct, and the other would just return distinct prod_amt's where the records are duplicates.
While the above example is nice if you truly have different aggregation needs depending on complex logic, for your question there's a simpler way of doing the same thing that doesn't use window functions:
SELECT
acct_nbr,
sum(prod_amt) AS amt
FROM
(
SELECT DISTINCT
acct_nbr,
prod_amt
FROM
table
)t1
GROUP BY 1
If you need to adapt this to a complex statement you could just sling your complex statement in as subquery where table is above like:
SELECT
acct_nbr,
sum(prod_amt) AS amt
FROM
(
SELECT DISTINCT
acct_nbr,
prod_amt
FROM
(
YOUR REALLY COMPLEX QUERY GOES IN HERE
)t2
)t1
GROUP BY 1
I will demonstrate a simple example of my issue instead of viewing the long sql query from my project.
My example is something like:
Select DeliveryAddressID, PickupDate, TotalWeight, count(DeliveryAddressID) AS Packages
From DeliveryDB
Where Date1 >= '10-12-2013' AND Date2 <= '15-12-2013'
Group By PickupDate, DeliveryAddressID
My result will be something like:
http://prntscr.com/1iksoe
What I want to achieve is to get this DeliveryAddressID for ALL 4 packages but still keep the total numbers. This would be something like this.
What I want as expected result:
http://prntscr.com/1iksr7
The reason I want to do this is because in this way i will be able to track the barcode for each package and also be able to track more details.
Cheers :-)
In most databases, you can do this using window functions:
Select DeliveryAddressID, PickupDate, TotalWeight,
count(DeliveryAddressID) over (partition by PickupDate, DeliveryAddressID) AS Packages
From DeliveryDB
Where Date1 >= '10-12-2013' AND Date2 <= '15-12-2013';
This will return the total on all four lines.
EDIT:
You can handle TotalWeight (which was not part of the original question) the same way:
Select DeliveryAddressID, PickupDate,
count(DeliveryAddressID) over (partition by PickupDate, DeliveryAddressID) AS Packages,
CEILING(SUM(CASE When (Weight <> 0 AND Volumen <> 0) AND Weight >= (Volumen * #zeroVoluFac)
Then Weight
end) over (partition by PickupDate, DeliveryAddressID)
) as TotalWeight
From DeliveryDB
Where Date1 >= '10-12-2013' AND Date2 <= '15-12-2013';
I can't do quite that, but would you accept total lines after the details instead of zero on the first N lines and the total on the last, like this:
IdCode Weight PickupDate
----------- ----------- -------------------------
12345 5 2013-01-07 00:00:00.0
12345 10 2013-01-07 00:00:00.0
12345 15 2013-01-07 00:00:00.0
12345 20 2013-01-07 00:00:00.0
(4 rows)
Sum(Weight) Count(IdCode)
----------- -------------
50 4
If that's of any use to you, the SQL I used was (sorry about the non-matching column names)
Select IdCode, Weight, PickupDate
From DeliveryDB
Group By PickupDate, IdCode
ORDER BY PickupDate, IdCode
COMPUTE sum(Weight), count(IdCode) by PickupDate, IdCode
The COMPUTE gives totals or other aggregates at the breaks in Group By.
I'm trying to improve a transit scheduling table by adding a column and flagging some rows to indicate they are the last stop for each trip.
Each trip will have many rows showing its stops and their sequence along the trip. I want to update the LastStop column with a '1' if the Sequence number is the highest for that trip.
I think the following SQL is on the right track but I am getting a "no such column: s1.stop_sequence" so I have no idea if I'm even on the right track until this unobvious to me error is resolved. I am a SQL lightweight barely beyond novice level. Stop_Sequence is definitely the correct name for the column.
UPDATE stop_times
SET LastStop = '1'
WHERE stop_sequence =(
SELECT max(st.stop_sequence)
FROM stop_times s1
WHERE s1.trip_id = trip_id
)
AND
trip_id = s1.trip_id
AND
stop_ID = s1.stop_id;
A simplified version of sample data is below.
TripID Stop Sequence LastStop
665381 1766 1
665381 3037 2
665381 3038 3 1
667475 1130 1
667475 2504 2 1
644501 2545 1
644501 3068 2
644501 2754 3
644501 3069 4
644501 2755 5 1
You cannot refer to a column in the subquery from the outer query.
Furthermore, the filter trip_id = s1.trip_id is duplicated, and you do not want to filter on stop_id because that would prevent the MAX from looking at any other stops of the trip.
Try this:
UPDATE stop_times
SET LastStop = '1'
WHERE Stop_Sequence = (SELECT MAX(Stop_Sequence)
FROM stop_times s1
WHERE s1.Trip_ID = stop_times.Trip_ID)
Alternatively, a last stop is a stop for which no other stop with a larger sequence number in the same trip exists:
UPDATE stop_times
SET LastStop = '1'
WHERE NOT EXISTS (SELECT 1
FROM Stop_Sequence s1
WHERE s1.Trip_ID = stop_times.Trip_ID
AND s1.Stop_Sequence > stop_times.Stop_Sequence)
This will work for you, as long as stops field is always less than 1000 (use bigger multiplier if it is):
UPDATE stop_times
SET laststop = 1
WHERE tripid*1000+sequence IN (
SELECT tripid*1000+sequence FROM (
SELECT tripid, max(sequence) AS sequence
FROM stop_times
GROUP BY 1
)
)
I would have written this using tuple syntax, but SQLite does not support it:
UPDATE stop_times
SET laststop = 1
WHERE (tripid, sequence) IN (
SELECT (tripid, sequence) FROM (
SELECT tripid, max(sequence) AS sequence
FROM stop_times
GROUP BY 1
)
)
Sorry, no SQLFiddle - it does not seem to work for me today.
I am using SQLite 3.7.4 and have a table that includes:
TABLE: 'Orders'
ID OrderDate
1234 2011-06-01 00:00:00
1245 2011-06-04 00:00:00
1234 2011-06-05 00:00:00
I'd like to be able to select the 'OrderDate' & 'ID' where, for a given OrderDate there is a previous order in the last 1 week.
So, for the data above:
The first record ID 1234 has an order
on 2011-06-01 00:00:00, but has none
previous - so isn't selected.
The next record ID 1245 has an order
on 2011-06-04 00:00:00 but none in
week prior to 2011-06-04, so not
selected.
The 3rd record ID 1234 order made on
2011-06-05 00:00:00 has a previous
order on 2011-06-01, so this record
is selected.
I have just about managed to get my head around using strftime('%s',OrderDate) for date differences,but can't work out how to query by taking the yyyy-mm-dd part of the OrderDate record and looking back '-7 days' to see if there are 1 or more records within that range ?
Any guideance appreciated :)
Not certain I understand, but this gives the output you're looking for, given the sample data.
sqlite> select o1.id, o1.orderdate from orders o1
...> inner join orders o2 on (o1.id = o2.id
...> and o2.orderdate >= date(o1.orderdate, '-7 day')
...> and o2.orderdate < o1.orderdate);
1234|2011-06-05
You should probably look at SQLite Date and Time Functions.