SELECT STATEMENT WITH SUBQUERY date - 30 days - sql

In SQL Server 20112 I need to select the row ID from the dbo.table where the date is 30 days older than the date of the last record (the last record does not have to be today's date!).
SO....
The child query to get the right date is as follows:
SELECT cast(max(table_time) - 30 as datetime)
FROM dbo.table
WHERE column_value = 105
This returns exactly one [date] value as string '2014-02-03 ....'
Next I use this child query as a sub-query for the parent SELECT statement:
SELECT max(row_id)
FROM dbo.table
WHERE table_time = (SELECT cast(max(table_time) - 30 as datetime)
FROM dbo.table
WHERE column_value = 105)
...but this does not return the row_id I am looking for (it returns NULL). I tried to cast the date as follows but got the same NULL result
SELECT max(row_id)
FROM dbo.table
WHERE table_time = cast((SELECT cast(max(table_time) - 30 as datetime)
FROM dbo.table
WHERE column_value = 105) as datetime)
I suppose that if the child query returns a valid date value I should be able to use it in the parent select statement as a value I could compare/validate against.
Where I am wrong ?

---Use the 'cast' function in the 'where' clause as well
SELECT max(row_id) FROM dbo.table
WHERE cast(table_time as datetime)=
(SELECT cast(max(table_time) - 30 as datetime)
FROM dbo.table WHERE column_value = 105)

Related

iSeries SQL - Use Previous Record Value in JOIN criteria

In my example, I am trying to retrieve records from a transaction file based on the previous calendar work date. The work dates are in a file called CALNDR, the transactions in a file called TRNHST. The logic would be to use the CURRENT_DATE to set the cursor on CALNDR, then retrieve the previous work date which will be used to join to TRNHST. This would be simple enough if done in RPG but I am at a loss with SQL. Thanks, for any input you can provide.
CALNDR
TRNHST
This is even more simple with SQL probably.
You may run these statements as is to check.
LAG function use
WITH
CALNDR (CALDT) AS
(
VALUES
CAST ('2022-07-21' AS DATE)
, CAST ('2022-07-22' AS DATE)
, CAST ('2022-07-25' AS DATE)
)
SELECT C.*
FROM
(
SELECT CALDT, LAG (CALDT) OVER (ORDER BY CALDT) AS LAG_CALDT
FROM CALNDR
) C
-- JOIN TRNHST ...
-- You must keep this expression in WHERE >>OUTSIDE<< of the subselect
-- to get the desired result
WHERE C.CALDT =
CAST ('2022-07-25' AS DATE)
--CAST ('2022-07-24' AS DATE)
;
The problem with LAG is that you can't use it, if you have non-continuous calendar as in your case, and CURRENT_DATE for some day (as 2022-07-24 in the example) doesn't exist there.
BTW, I don't know if it's some problem namely with my IBM i v7.5 or not, but if I move the WHERE clause to the subselect, I get wrong result of the query at all - it doesn't achieve the goal desired here and doesn't return the expected result.
Aggregation function use
WITH
CALNDR (CALDT) AS
(
VALUES
CAST ('2022-07-21' AS DATE)
, CAST ('2022-07-22' AS DATE)
, CAST ('2022-07-25' AS DATE)
)
SELECT C.*
FROM
(
SELECT MAX (CALDT) AS LAG_CALDT
FROM CALNDR
WHERE CALDT <
CAST ('2022-07-25' AS DATE)
--CAST ('2022-07-24' AS DATE)
) C
-- JOIN TRNHST
;
Works disregarding of the presence of CURRENT_DATE in the calendar.
Honestly, if "prior working day" for a given date is something you need. You should consider adding such a column to your calendar table.
While you're at it, might as well add a "next working day".
One of the biggest benefits to a calendar table is the ability to pre-calculate such columns. Thus, greatly simplifying your statements.
Not real sure what LAG does as I have never used it. So, here's another solution using a cross join:
WITH calendar (workdt) AS (
VALUES (CAST('2022-01-01' AS date)),
(CAST('2022-02-01' AS date)),
(CAST('2022-03-01' AS date)),
(CAST('2022-04-01' AS date))
),
orders (orderno, orderdt) AS (
VALUES (1, CAST('2022-01-15' AS date)),
(2, CAST('2022-02-15' AS date)),
(3, CAST('2022-03-15' AS date)),
(4, CAST('2022-04-15' AS date))
)
SELECT *
FROM orders a
CROSS JOIN LATERAL (SELECT max(workdt) prevdt FROM calendar WHERE workdt < a.orderdt) b
The cross join only returns a single row for each record in the calendar file, and that row contains the largest work date less than the order date. LATERAL just lets the sub-query access columns from the outer query.
write an SQL function named calndr_prevWorkDate that returns the previous work date.
Then use that function in the WHERE clause of an SQL SELECT statement to select records from the TRNHST table:
select a.*
from trnhst a
where a.trandate = calndr_prevWorkDate( )
Here is the SQL function.
CREATE OR REPLACE FUNCTION calndr_prevWorkDate(
startDate date default current date )
RETURNS date
language sql
specific calndrf1
SET OPTION datfmt = *ISO, DLYPRP = *YES, DBGVIEW = *SOURCE,
USRPRF = *OWNER, DYNUSRPRF = *OWNER, COMMIT = *CHG
BEGIN
declare vCalDate date ;
declare vSqlCode decimal(5,0) default 0 ;
declare sqlCode int default 0 ;
declare sqlState char(5) default ' ' ;
declare vSqlState char(5) default ' ' ;
declare vErrText varchar(256) default '' ;
DECLARE CONTINUE HANDLER FOR SQLEXCEPTION
begin
SET vSqlCode = SQLCODE ;
SET vSqlState = SQLstate ;
get diagnostics exception 1 vErrText = message_text ;
end ;
select a.caldate
into vCalDate
from calndr a
where a.caldate < startDate
and dayofweek(a.caldate) not in ( 1, 7)
order by a.caldate desc
fetch first row only ;
return vCalDate ;
END
if all u need is the previous date from a table
you can use something like that
select *
from TRNHST
where Trans_Date =
(
select calendar_Date
from CALNDR
where calendar_Date < current date
order by calendar_Date desc
limit 1
)

How to add an extra temp date column using the select query in SQL Server

I am trying to add an extra date column in the select statement using a certain where condition.
Below is my current table:
table
I want to add an extra Date column which is add all date between >=Start and <= End-2.
output
Getting error with this query:
SELECT
*, temp_Date AS Date
FROM
Mytable
WHERE
Date >= Start AND Date <= End - 2
Thanks in advance.
Consider this statement as dummy data:
CREATE TABLE MyTable
(
id int not null,
startDate date not null,
endDate date not null,
val int not null
)
insert into MyTable
values
(10,'20171106','20171112',7),
(10,'20171106','20171112',6),
(10,'20171106','20171112',5),
(10,'20171106','20171112',0),
(10,'20171106','20171112',2)
Using recursive CTE you select each tuple as your starting date and increment that date until it reaches the enddate like this:
;WITH rc AS (
SELECT id, startDate, endDate, val
, startDate AS temp_date
FROM MyTable
UNION ALL
SELECT id, startDate, endDate, val
, DATEADD(DAY,1,temp_date)
FROM rc
WHERE DATEADD(DAY,1,temp_date) <= enddate
)
SELECT *
FROM rc
You should be aware the recursion in SQL-Server is expensive and slow on larger data. Also remember to hint the maximum recursive loop amounts as the default is 100. Example:
OPTION (MAXRECURSION 0)
The 0 would be unlimited recursions, with the risk of running infinitely.
As I read you are using a data warehouse and as such it should have a time or date dimension. In such case a simple join would do the work:
SELECT id, startDate, endDate, val
, date_sid AS temp_date
FROM MyTable AS m
INNER JOIN DimDate AS dd
ON dd.date_sid >= startDate
AND dd.date_sid <= endDate
Please consider not using reserved keys for column names (like start, end or value)
Try this:
Select M.*
, Temp_date = Datediff(day, M.start, dateadd(day,-2,M.end))
from MyTable M

Ignoring Duplicate Records SQL

In need of some help :)
So I have a table of records with the following columns:
Key (PK, FK, int) DT (smalldatetime) Value (real)
The DT is a datetime for every half hour of the day with an associated value
E.g.
Key DT VALUE
1000 2010-01-01 08:00:00 80
1000 2010-01-01 08:30:00 75
1000 2010-01-01 09:00:00 100
I have a Query that finds the max value every 24 hour period and its associated time however, on one day the max value occurs twice and hence duplicates the date which is causing processing issues. I have tried using rownumber() which works but I can't use a calculated column in my where clause?
Currently I have:
SELECT cast(T1.DT as date) as 'Date',Cast(T1.DT as time(0)) as 'HH', ROW_NUMBER() over (PARTITION BY cast(DT as date) ORDER BY DT) AS 'RowNumber'
FROM TABLE_1 AS T1
INNER JOIN (
SELECT CAST([DT] as date) as 'DATE'
, MAX([VALUE]) as 'MAX_HH'
FROM TABLE_1
WHERE DT > '6-nov-2016' and [KEY] = '1000'
GROUP BY CAST([DT] as date)
) AS MAX_DT
ON MAX_DT.[DATE] = CAST(T1.[DT] as date)
AND T1.VALUE = MAX_DT.MAX_HH
WHERE DT > '6-nov-2016' and [KEY] = '1000'
ORDER BY DT
This results in
Key DT VALUE HH
1000 2010-01-01 80 07:00:00
1000 2010-02-01 100 17:30:00
1000 2010-02-01 100 18:00:00
I need to remove the duplicate date (I Have no preference which HH it takes)
I think I've explained that terribly, let me know if it makes no sense and i'll try and re write
Any ideas?
Can you try this the new code is in ** **:
SELECT cast(T1.DT as date) as 'Date', ** MIN(Cast(T1.DT as time(0))) as 'HH' **
FROM TABLE_1 AS T1
INNER JOIN (
SELECT CAST([DT] as date) as 'DATE'
, MAX([VALUE]) as 'MAX_HH'
FROM TABLE_1
WHERE DT > '6-nov-2016' and [KEY] = '1000'
GROUP BY CAST([DT] as date)
) AS MAX_DT
ON MAX_DT.[DATE] = CAST(T1.[DT] as date)
AND T1.VALUE = MAX_DT.MAX_HH
WHERE DT > '6-nov-2016' and [KEY] = '1000'
here put the group by
GROUP BY cast(T1.DT as date)
ORDER BY DT
i would do something like this
i didnt try it but i think it s correct.
SELECT cast(T1.DT as date) as 'Date',Cast(T1.DT as time(0)) as 'HH', VALUE
FROM TABLE_1 T1
WHERE [DT] IN (
--select the max date from Table_1 for each day
SELECT MAX([DT]) max_date FROM TABLE_1
WHERE (CAST([DT] as date) ,value) IN
(
SELECT CAST([DT] as date) as 'CAST_DATE'
,MAX([VALUE]) as 'MAX_HH'
FROM TABLE_1
WHERE DT > '6-nov-2016' and [KEY] = '1000'
GROUP BY CAST([DT] as date
)group by [DT]
)
WHERE DT > '6-nov-2016' and [KEY] = '1000'
Change the JOIN to an APPLY.
The APPLY operation will allow you to limit the connected relation to just one result for each source relation.
SELECT v.[Key], cast(v.DT As Date) as "Date", v.[Value], cast(v.DT as Time(0)) as "HH"
FROM
( -- First a projection to get just the exact dates you want
SELECT DISTINCT [Key], CAST(DT as DATE) as DT
FROM Table_1
WHERE [Key] = '1000' AMD DT > '20161106'
) dates
CROSS APPLY (
-- Then use APPLY rather than JOIN to find just the exact one record you need for each date
SELECT TOP 1 *
FROM Table_1
WHERE [Key] = dates.[Key] AND cast(DT as DATE) = dates.DT ORDER BY [Value] DESC
) v
A final note: Both this query and your sample query in the question will include values from Nov 6, 2016. The query says > 2016-11-05 with an exlusive inequality, but the original was still comparing using full DateTime values, meaning there is a implied 0 as a time component. So 12:01 AM on Nov 6 is still greater than 12:00:00.001 AM on Nov 6. If you want to exclude all Nov 6 dates from the query, you either need to change this to use a time value at the end of the date, or cast to date before making that > comparison.
With SQL you can use SELECT DISTINCT,
The SELECT DISTINCT statement is used to return only distinct (different) values.
Inside a table, a column often contains many duplicate values; and sometimes you only want to list the different (distinct) values.
The SELECT DISTINCT statement is used to return only distinct (different) values.

How to select the current date row from multiple date rows using system date

I have a table with many rows, they contain different dates, any one of them will be for the current period. There is no end date as a field otherwise i would have compared system date between from and to date. I have tried using max function but still it displays many rows.
The data is grouped by a type identifier, so for each type there will be a current date row.
What can be the best query to get the current row (single) which is active considering the current date?
Below is the original query:
Select Group1,Group2,FromDate,FPFrom, FpTo FROM [DB].[dbo].[HGD] AS GD, [DB].[dbo].[HDT] AS TD WHERE GD.GRoup1 = TD.MainGroup
Thanks
SELECT TOP 1 * FROM yourTable WHERE procStart <= getdate() ORDER BY procStart DESC
or something like
SELECT * FROM (SELECT TOP 1 * FROM yourTable row_number OVER(GROUP BY TypeId, Order By procStart DESC) RN WHERE procStart <= getdate()) DQ WHERE DQ.RN = 1
Please try to be more precise. I think you are looking something like shown below:
CREATE TABLE #temp(
SomeDate datetime,
SomeType int
)
INSERT #temp VALUES
('2016-07-20', 1),
('2016-07-23', 1),
('2016-07-27', 1),
('2016-07-30', 1),
('2016-01-25', 3),
('2016-01-31', 3),
('2016-02-21', 3),
('2016-07-23', 3),
('2016-09-30', 3)
WITH Numbered AS
(
SELECT SomeDate, SomeType, ROW_NUMBER() OVER (PARTITION BY SomeType ORDER BY SomeDate) RowNumber
FROM #temp
),
Ranges AS
(
SELECT T1.SomeDate StartPeriod, COALESCE(T2.SomeDate, DATEADD(year,1,GETDATE())) EndPeriod, T1.SomeType
FROM Numbered T1
LEFT JOIN Numbered T2 ON T1.RowNumber+1=T2.RowNumber AND T1.SomeType=T2.SomeType
)
SELECT * FROM Ranges
WHERE GETDATE() BETWEEN StartPeriod AND EndPeriod
ORDER BY SomeType
This yields:
StartPeriod EndPeriod SomeType
2016-07-23 00:00:00.000 2016-07-27 00:00:00.000 1
2016-07-23 00:00:00.000 2016-09-30 00:00:00.000 3
#Paweł Dyl gave me an idea and I added a condition to my query and got the desired results.
ToDate field was not available , so I created a field by adding 180 days to it.
AND GetDate() BETWEEN cast(FromDate as Date) AND DATEADD(DAY, 180,cast(FromDate as DATE))
Thanks again.

force number of rows to return in date range from SQL query

I'm running a query on our SQL (2012) database which returns a count of records in a given date range, grouped by the date.
For example:
Date Count
12/08 12
14/08 19
19/08 11
I need to fill in the blanks as the charts I plot get screwed up because there are missing values. Is there a way to force the SQL to report back a blank row, or a "0" value when it doesn't come across a result?
My query is
SELECT TheDate, count(recordID)
FROM myTable
WHERE (TheDate between '12-AUG-2013 00:00:00' and '20-AUG-2013 23:59:59')
GROUP BY TheDate
Would I need to create a temp table with the records in, then select from that and right join any records from myTable?
Thanks for any help!
If you create a (temporary or permanent) table of the date range, you can then left join to your results to create a result set including blanks
SELECT dates.TheDate, count(recordID)
FROM
( select
convert(date,dateadd(d,number,'2013-08-12')) as theDate
from master..spt_values
where type='p' and number < 9
) dates
left join yourtable on dates.thedate = convert(date,yourtable.thedate)
GROUP BY dates.TheDate
A temp table would do the job but for such a small date range you could go even simpler and use a UNION-ed subquery. E.g:
SELECT dates.TheDate, ISNULL(counts.Records, 0)
FROM
(SELECT TheDate, count(recordID) AS Records
FROM myTable
WHERE (TheDate between '12-AUG-2013 00:00:00' and '20-AUG-2013 23:59:59')
GROUP BY TheDate
) counts
RIGHT JOIN
(SELECT CAST('12-AUG-2013' AS DATETIME) AS TheDate
UNION ALL SELECT CAST('13-AUG-2013' AS DATETIME) AS TheDate
UNION ALL SELECT CAST('14-AUG-2013' AS DATETIME) AS TheDate
UNION ALL SELECT CAST('15-AUG-2013' AS DATETIME) AS TheDate
UNION ALL SELECT CAST('16-AUG-2013' AS DATETIME) AS TheDate
UNION ALL SELECT CAST('17-AUG-2013' AS DATETIME) AS TheDate
UNION ALL SELECT CAST('18-AUG-2013' AS DATETIME) AS TheDate
UNION ALL SELECT CAST('19-AUG-2013' AS DATETIME) AS TheDate
UNION ALL SELECT CAST('20-AUG-2013' AS DATETIME) AS TheDate
) dates
ON counts.TheDate = dates.TheDate
Here's a SQL Fiddle Demo.
If you need a more generic (but also more complex) solution, take a look at this excellent answer (by #RedFilter) to a similar question.