I have three tables.
The first one is Device table
+----------+------+
| DeviceId | Type |
+----------+------+
| 1 | 10 |
| 2 | 20 |
| 3 | 30 |
+----------+------+
The second one is History table - data received by different devices.
+----------+-------------+--------------------+
| DeviceId | Temperature | TimeStamp |
+----------+-------------+--------------------+
| 1 | 31 | 15.08.2020 1:42:00 |
| 2 | 100 | 15.08.2020 1:42:01 |
| 2 | 40 | 15.08.2020 1:43:00 |
| 1 | 32 | 15.08.2020 1:44:00 |
| 1 | 34 | 15.08.2020 1:45:00 |
| 3 | 20 | 15.08.2020 1:46:00 |
| 2 | 45 | 15.08.2020 1:47:00 |
+----------+-------------+--------------------+
The third one is DeviceStatusHistory table
+----------+---------+--------------------+
| DeviceId | State | TimeStamp |
+----------+---------+--------------------+
| 1 | 1(OK) | 15.08.2020 1:42:00 |
| 2 | 1(OK) | 15.08.2020 1:43:00 |
| 1 | 1(OK) | 15.08.2020 1:44:00 |
| 1 | 0(FAIL) | 15.08.2020 1:44:30 |
| 1 | 0(FAIL) | 15.08.2020 1:46:00 |
| 2 | 0(FAIL) | 15.08.2020 1:46:10 |
+----------+---------+--------------------+
I want to select the last temperature of devices, but take into account only those history records that occurs until the first device failure.
Since device1 starts failing from 15.08.2020 1:44:30, I don't want its records that go after that timestamp.
The same for the device2.
So as a final result, I want to have only data of all devices until they get first FAIL status:
+----------+-------------+--------------------+
| DeviceId | Temperature | TimeStamp |
+----------+-------------+--------------------+
| 2 | 40 | 15.08.2020 1:43:00 |
| 1 | 32 | 15.08.2020 1:44:00 |
| 3 | 20 | 15.08.2020 1:46:00 |
+----------+-------------+--------------------+
I can select an appropriate history only if device failed at least once:
SELECT * FROM Device D
CROSS APPLY
(SELECT TOP 1 * FROM History H
WHERE D.Id = H.DeviceId
and H.DeviceTimeStamp <
(select MIN(UpdatedOn) from DeviceStatusHistory Y where [State]=0 and DeviceId=D.Id)
ORDER BY H.DeviceTimeStamp desc) X
ORDER BY D.Id;
The problems is, if a device never fails, I don't get its history at all.
Update:
My idea is to use something like this
SELECT * FROM DeviceHardwarePart HP
CROSS APPLY
(SELECT TOP 1 * FROM History H
WHERE HP.Id = H.DeviceId
and H.DeviceTimeStamp <
(select ISNULL((select MIN(UpdatedOn) from DeviceMetadataPart where [State]=0 and DeviceId=HP.Id),
cast('12/31/9999 23:59:59.997' as datetime)))
ORDER BY H.DeviceTimeStamp desc) X
ORDER BY HP.Id;
I'm not sure whether it is a good solution
You can use COALESCE: coalesce(min(UpdateOn), cast('9999-12-31 23:59:59' as datetime)). This ensures you always have an upperbound for your select instead of NULL.
I will treat this as two parts problem
I will try to find the time at which device has failed and if it hasn't failed I will keep it as a large value like some timestamp in 2099
Once I have the above I can simply join with histories table and take the latest value before the failed timestamp.
In order to get one, I guess there can be several approaches. From top of my mind something like below should work
select device_id, coalesce(min(failed_timestamps), cast('01-01-2099 01:01:01' as timestamp)) as failed_at
(select device_id, case when state = 0 then timestamp else null end as failed_timestamps from History) as X
group by device_id
This gives us the minimum of failed timestamp for a particular device, and an arbitrary large value for the devices which have never failed.
I guess after this the solution is straight forward.
I have a 1 table in a db that stored Incoming, Outgoing and Net values for various Account Codes over time. Although there is a date field the sequence of events per Account Code is based on the "Version" number where 0 = original record for each Account Code and it increments by 1 after each change to that Account Code.
The Outgoing and Incoming values are stored in the db as cumulative values rather than the individual transaction value but I am looking for a way to Select * From this table and return the individual amounts as opposed to the cumulative.
Below are test scripts of table and data, and also 2 examples.
If i Select where code = '123' in the test table I currently get this (values are cumulative);
+------+------------+---------+---------+---------+-----+
| Code | Date | Version | Incoming| Outgoing| Net |
+------+------------+---------+---------+---------+-----+
| 123 | 01/01/2018 | 0 | 100 | 0 | 100 |
| 123 | 07/01/2018 | 1 | 150 | 0 | 150 |
| 123 | 09/01/2018 | 2 | 150 | 100 | 50 |
| 123 | 14/01/2018 | 3 | 200 | 100 | 100 |
| 123 | 18/01/2018 | 4 | 200 | 175 | 25 |
| 123 | 23/01/2018 | 5 | 225 | 175 | 50 |
| 123 | 30/01/2018 | 6 | 225 | 225 | 0 |
+------+------------+---------+---------+---------+-----+
This is what I would like to see (each individual transaction);
+------+------------+---------+----------+----------+------+
| Code | Date | Version | Incoming | Outgoing | Net |
+------+------------+---------+----------+----------+------+
| 123 | 01/01/2018 | 0 | 100 | 0 | 100 |
| 123 | 07/01/2018 | 1 | 50 | 0 | 50 |
| 123 | 09/01/2018 | 2 | 0 | 100 | -100 |
| 123 | 14/01/2018 | 3 | 50 | 0 | 50 |
| 123 | 18/01/2018 | 4 | 0 | 75 | -75 |
| 123 | 23/01/2018 | 5 | 25 | 0 | 25 |
| 123 | 30/01/2018 | 6 | 0 | 50 | -50 |
+------+------------+---------+----------+----------+------+
If I had the individual transaction values and wanted to report on the cumulative, I would use an OVER PARTITION BY, but is there an opposite to that?
I am not looking to redesign the create table or the process in which it is stored, I am just looking for a way to report on this from our MI environment.
Note: I've added other random Account Codes into this to emphasis how the data is not ordered by Code or Version, but by Date.
thanks in advance for any help.
USE [tempdb];
IF EXISTS ( SELECT *
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME = 'Table1'
AND TABLE_SCHEMA = 'dbo')
DROP TABLE [dbo].[Table1];
GO
CREATE TABLE [dbo].[Table1]
(
[Code] CHAR(3)
,[Date] DATE
,[Version] CHAR(3)
,[Incoming] DECIMAL(20,2)
,[Outgoing] DECIMAL(20,2)
,[Net] DECIMAL(20,2)
);
GO
INSERT INTO [dbo].[Table1] VALUES
('123','2018-01-01','0','100','0','100'),
('456','2018-01-02','0','50','0','50'),
('789','2018-01-03','0','0','0','0'),
('456','2018-01-04','1','100','0','100'),
('456','2018-01-05','2','150','0','150'),
('789','2018-01-06','1','50','50','0'),
('123','2018-01-07','1','150','0','150'),
('456','2018-01-08','3','200','0','200'),
('123','2018-01-09','2','150','100','50'),
('789','2018-01-10','2','0','0','0'),
('456','2018-01-11','4','225','0','225'),
('789','2018-01-12','3','75','25','50'),
('987','2018-01-13','0','0','50','-50'),
('123','2018-01-14','3','200','100','100'),
('654','2018-01-15','0','100','0','100'),
('456','2018-01-16','5','250','0','250'),
('987','2018-01-17','1','50','50','0'),
('123','2018-01-18','4','200','175','25'),
('789','2018-01-19','4','100','25','75'),
('987','2018-01-20','2','150','125','25'),
('321','2018-01-21','0','100','0','100'),
('654','2018-01-22','1','0','0','0'),
('123','2018-01-23','5','225','175','50'),
('321','2018-01-24','1','100','50','50'),
('789','2018-01-25','5','100','50','50'),
('987','2018-01-26','3','150','150','0'),
('456','2018-01-27','6','250','250','0'),
('456','2018-01-28','7','270','250','20'),
('321','2018-01-29','2','100','100','0'),
('123','2018-01-30','6','225','225','0'),
('987','2018-01-31','4','175','150','25')
;
GO
SELECT *
FROM [dbo].[Table1]
WHERE [Code] = '123'
GO;
USE [tempdb];
IF EXISTS ( SELECT *
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME = 'Table1'
AND TABLE_SCHEMA = 'dbo')
DROP TABLE [dbo].[Table1];
GO;
}
Just use lag():
select Evt, Date, Version,
(Loss - lag(Loss, 1, 0) over (partition by evt order by date)) as incoming,
(Rec - lag(Rec, 1, 0) over (partition by evt order by date)) as outgoing,
(Net - lag(Net, 1, 0) over (partition by evt order by date)) as net
from [dbo].[Table1];
Basically, I want to achieve , for each months, like in this example, from January until March 2013, what is the Max(Most_Recent_Day) for each users.
Example, From January to March, every month in the Database, systems will capture the Most_Recent_Day for each users.
Below are the expected results:
User | Most_Recent_Day
--------------------------------
afolabi.banu | 1/31/2013
afolabi.banu | 2/7/2013
afolabi.banu | 3/21/2013
mario.sapiter | 1/22/2013
mario.sapiter | 2/7/2013
mario.sapiter | 3/11/2013
However, I want to have another DB column as well to be display .Below is the column.
User|Total_Hits | Recent_Month| Most_Recent_Day | Most_Recent_Days_Hits
I tried to use inner join, but the result are not what i expect. I got duplicated user name and duplicated recent day. Basically, I want only to display no duplicated record for the same user name.
Below is the result that I got. Please ignore the recent_month value since it's data from database.
User |Total_Hits | Recent_Month | Most_Recent_Day | Most_Recent_Days_Hits
-------------------------------------------------------------------------------------
afolabi.banu | 223 | 25 | 2/7/2013 | 5
afolabi.banu | 223 | 25 | 2/7/2013 | 5
afolabi.banu | 211 | 13 | 1/31/2013 | 3
afolabi.banu | 223 | 25 | 2/7/2013 | 5
afolabi.banu | 296 | 31 | 3/21/2013 | 1
afolabi.banu | 296 | 31 | 3/21/2013 | 1
mario.sapiter | 95 | 7 | 2/7/2013 | 5
mario.sapiter | 7 | 7 | 3/21/2013 | 1
mario.sapiter | 7 | 37 | 3/22/2013 | 1
mario.sapiter | 249 | 37 | 2/7/2013 | 5
This is my SQL Code
SELECT t.[User],
t.Total_Hits,
t.Recent_Month,
t.Most_Recent_Day,
t.Most_Recent_Day_Hits FROM UserUsageMonthly t
INNER JOIN
(
select
[User]
, max(Most_Recent_Day) as Most_Recent_Day
from UserUsageMonthly (NoLock)
where Application_Name='Daily Production Review' and Site_Collection='wrm13'
and Most_Recent_Day between '1/1/2013' and '3/31/2013'
group by [User], datepart(month,Most_Recent_Day)
) table2
ON
t.[User]=table2.[User]
AND t.Most_Recent_Day = table2.Most_Recent_Day
order by t.[User]
You should add the month value to your SQL SELECT
SELECT
MONTH(t.Most_Recent_Day) as 'MyMonth',
t.[User],
t.Total_Hits,
t.Recent_Month,
t.Most_Recent_Day,
t.Most_Recent_Day_Hits FROM UserUsageMonthly t
Then you can group by the month column
GROUP BY MyMonth
We have a phone dialer who call us store to inform them about gas price in their region.
We have 3 tables (WBDAPP00,WBDCIE00,WBDCIA00)
WBDAPP00 is where we store information about the call.
DANOID = ID
DA#INT,DA#IND,DA#TEL = phone number
DA#ENV = The number of group call, we send 1 message to few store.
DASTAT = The status of the call (Confirm by store,canceled,running, confirmed by us, in pause)
DADTHR = The timestamp of the last status modification
WBDCIE00 is where we store information about the group of store
CIE#EN = ID
CIEDHC = The timestamp where the call is effective, we can call the morning to tell the price will change at 14h30
CIE$OR = The price for regular
CIE$PL = The price for plus
CIE$SP = The price for super
CIE$DI = The price for diesel
WBDCIA00 is complementary information about WBDAPP00
CIA#ST = The ID of the store
CIA#AP = The ID of the call
CIE#EN = The ID of the group call
CIABAN = This is the number of the compagny of the store
This is a sample output of these 3 tables
SELECT * FROM PRDCM/WBDAPP00 WHERE DA#ENV = 17258 OR DA#ENV = 17257
+--------+--------+--------+---------+--------+--------+----------------------------+-----------+--------+
| DANOID | DA#INT | DA#IND | DA#TEL | DA#ENV | DASTAT | DADTHR | DAPARM | DAMUSR |
+--------+--------+--------+---------+--------+--------+----------------------------+-----------+--------+
| 100420 | 1 | 418 | 9600055 | 17257 | 4 | 2012-05-07-09.15.04.768228 |1;2;1;1;1;1| ISALAP |
| 100421 | 1 | 819 | 7346491 | 17258 | 0 | 2012-05-07-09.23.32.362971 |0;4;0;1;0;0| ISALAP |
| 100422 | 1 | 819 | 7624747 | 17258 | 1 | 2012-05-07-09.24.28.042330 |0;3;1;1;0;1| ISALAP |
| 100423 | 1 | 819 | 6377874 | 17258 | 0 | 2012-05-07-09.23.32.803073 |0;3;0;1;0;1| ISALAP |
| 100424 | 1 | 819 | 8742844 | 17258 | 1 | 2012-05-07-09.24.25.347116 |1;1;1;1;0;1| ISALAP |
| 100425 | 1 | 819 | 8255744 | 17258 | 0 | 2012-05-07-09.23.33.207688 |1;3;1;1;0;1| ISALAP |
+--------+--------+--------+---------+--------+--------+----------------------------+-----------+--------+
SELECT * FROM PRDCM/WBDCIE00 WHERE CIE#EN = 17258 OR CIE#EN = 17257
+--------+----------------------------+--------+--------+--------+--------+
| CIE#EN | CIEDHC | CIE$OR | CIE$PL | CIE$SP | CIE$DI |
+--------+----------------------------+--------+--------+--------+--------+
| 17257 | 2012-05-04-17.00.00.000000 | 0 | 0 | 0 | 1,359 |
| 17258 | 2012-05-07-09.30.00.000000 | 1,354 | 0 | 0 | 0 |
+--------+----------------------------+--------+--------+--------+--------+
SELECT * FROM PRDCM/WBDCIA00 WHERE CIA#EN = 17258 OR CIA#EN = 17257
+--------+--------+--------+--------+
| CIA#ST | CIA#AP | CIA#EN | CIABAN |
+--------+--------+--------+--------+
| 96 | 100420 | 17257 | 2 |
| 316 | 100421 | 17258 | 4 |
| 320 | 100422 | 17258 | 3 |
| 321 | 100423 | 17258 | 3 |
| 338 | 100424 | 17258 | 1 |
| 366 | 100425 | 17258 | 3 |
+--------+--------+--------+--------+
This is the relation between tables
CIA#AP = DANOID
CIA#EN = CIE#EN = DA#ENV
I want to extract the last CIE$OR (not 0) and the last CIE$DI (not 0) for each CIA#ST.
The last one is determined by CIEDHC (Desc order).
DASTAT needs to be 1 or 4.
This is an example of want I want to extract from the data above :
+--------+--------+--------+
| CIA#ST | CIE$OR | CIE$DI |
+--------+--------+--------+
| 96 | 0 | 1,359 |
| 316 | 1,354 | 0 |
| 320 | 1,354 | 0 |
| 321 | 1,354 | 0 |
| 338 | 1,354 | 0 |
| 366 | 1,354 | 0 |
+--------+--------+--------+
Or like this one, that's not ideal but I will tolerate it in this case
+--------+-------------+-------+
| CIA#ST | productType | price |
+--------+-------------+-------+
| 96 | 3 | 1,359 |
| 316 | 6 | 1,354 |
| 320 | 6 | 1,354 |
| 321 | 6 | 1,354 |
| 338 | 6 | 1,354 |
| 366 | 6 | 1,354 |
+--------+-------------+-------+
For those who don't know AS400, FETCH FIRST 1 ROWS ONLY is equal to TOP 1 AND LIMIT 1
LAST does not exist in AS400 so I need to replace
SELECT LAST(Column1) AS test FROM table1
by
SELECT Column1,Column2 FROM table1 ORDER BY Column2 DESC LIMIT 1
I have tried with subselect but you can't use ORDER BY and FETCH FIRST 1 ROWS ONLY.
We are in V5R1 without any PTF.
This is an exemple of extraction
SELECT CIA#ST,CIE$OR,CIE$DI,CIEDHC
FROM PRDCM/WBDAPP03
INNER JOIN PRDCM/WBDCIE01 ON CIE#EN = DA#ENV
INNER JOIN PRDCM/WBDCIA01 ON CIA#AP = DANOID
WHERE DASTAT IN (1,4)
ORDER BY CIEDHC,DA#ENV
FETCH FIRST 5 ROWS ONLY
+--------+--------+--------+----------------------------+
| CIA#ST | CIE$OR | CIE$DI | CIEDHC |
+--------+--------+--------+----------------------------+
| 88 | 1,014 | 1,039 | 2010-08-25-09.00.00.000000 |
| 89 | 1,014 | 1,039 | 2010-08-25-09.00.00.000000 |
| 90 | 1,014 | 1,039 | 2010-08-25-09.00.00.000000 |
| 91 | 1,014 | 1,039 | 2010-08-25-09.00.00.000000 |
| 119 | 1,084 | 0 | 2010-08-25-09.00.00.000000 |
| 522 | 1,014 | 1,039 | 2010-08-25-09.00.00.000000 |
+--------+--------+--------+----------------------------+
I'll try all your suggestions.
Frankly, I'm a little twitchy about your schema here - there's some denormalization I'm not happy with, among other things (a multi-value column, really?). But you probably have a limited ability to change it, so... If possible, you should consider upgrading to at least V6R1 (which is what we're on), as the database gets more goodies. Thankfully, you still have CTEs, which will help a bit.
I'm assuming that what you want is the latest price change for a store (given by CIEDHC) with a call for that store in DASTAT as 1 or 4, not given by the call-time (so, what happens if an earlier group-call is 'confirmed' after a later one?). In other words, this isn't the last 'confirmed' change, it's the last 'entered' change.
I'm also assuming you have a 'store' table, with all the actual store ids defined. However, since you didn't list it, I created a CTE to manufacture one. You can (and probably should) swap it out in the resulting statement.
WITH Store (storeId) as (
SELECT DISTINCT cia#st
FROM Wbdcia00),
Price_Change (callGroup, occurredAt, productType, newPrice) as (
SELECT cie#en, ciedhc, 1, cie$or
FROM Wbdcie00
WHERE cie$or > 0
UNION ALL
SELECT cie#en, ciedhc, 4, cie$di
FROM Wbdcie00
WHERE cie$di > 0),
Confirmed_Changes (storeId, occurredAt, productType, newPrice) as (
SELECT WarehouseCall.cia#st, Change.occurredAt,
Change.productType, Change.newPrice
FROM Wbdcia00 as WarehouseCall
JOIN Wbdapp00 as Call
ON Call.danoid = WarehouseCall.cia#ap
AND Call.dastat IN (1, 4)
JOIN Price_Change as Change
ON Change.callGroup = da#env),
Latest_Change (storeId, productType, newPrice) as (
SELECT Actual.storeId, Actual.productType, Actual.newPrice
FROM Confirmed_Changes as Actual
EXCEPTION JOIN Confirmed_Changes as Remove
ON Remove.storeId = Actual.storeId
AND Remove.productType = Actual.productType
AND Remove.occurredAt > Actual.occurredAt)
SELECT store.storeId, COALESCE(Regular.newPrice, 0) as regularPrice,
COALESCE(Diesel.newPrice, 0) as dieselPrice
FROM Store
LEFT JOIN Latest_Change as Regular
ON Regular.storeId = Store.storeId
AND Regular.productType = 1
LEFT JOIN Latest_Change as Diesel
ON Diesel.storeId = Store.storeId
AND Diesel.productType = 4
Some things to note -
I figured you weren't actually giving a product a price of 0. This means that you're not looking for the individual call that went out, with both prices listed - you're going for the last change that happened, for each product. Which is why I pivoted/unpivoted that table like I did.
Needless to say, this statement reports the last entered change that was 'confirmed'. This is not the last confirmation of a change (indicated by dadthr), however.