SQL Server Current Date compare with specific date - sql-server

I got 1 table which is dbo.Invoice. My current query now is able to select "SalesRef" that does not have invoice for "Mvt_Type" = '122'. However, I need to extend my query with PostDate field.
My problem is current query still display an SalesRef that does not have invoice for "Mvt_Type" = '122' with Postdate today( 8/8/2017). My expected result is it can only be display if no invoice was made more than 2 days after the Postdate. So, it suppose to display on 11/8/2017 or more.
Table dbo.Invoice
| PO_NUMBER | TYPE | MVT_TYPE | QUANTITY | SALESREF | DEBIT | POSTDATE |
|----------- |------ |---------- |---------- |---------- |------- |------------ |
| 10001001 | GR | 101 | 1000.00 | 5001 | S | 2017-01-08 |
| 10001001 | GR | 101 | 2000.00 | 5002 | S | 2017-02-08 |
| 10001001 | GR | 122 | 1000.00 | 5001 | H | 2017-01-08 |
| 10001001 | INV | 000 | 1000.00 | 5001 | S | 2017-01-08 |
| 10001001 | INV | 000 | 2000.00 | 5002 | S | 2017-02-08 |
| 10001001 | GR | 122 | 1500.00 | 5002 | H | 2017-02-08 |
| 10001001 | INV | 000 | 1000.00 | 5001 | H | 2017-01-08 |
Below is my current query :
SELECT *
FROM dbo.INVOICE i
WHERE MVT_TYPE = '122' AND SALESREF IS NOT NULL AND POSTDATE > CONVERT(VARCHAR(10), dateadd(day,2,getdate()),101)
AND NOT EXISTS (SELECT 1
FROM dbo.INVOICE
WHERE DEBIT = 'H' AND MVT_TYPE = '000' AND SALESREF = i.SALESREF )
Expected Result is same like below. But this time need to add PostDate.
| PO_NUMBER | TYPE | MVT_TYPE | QUANTITY | SALESREF | DEBIT | POSTDATE |
|----------- |------ |---------- |---------- |---------- |------- |------------ |
| 10001001 | GR | 122 | 1500.00 | 5002 | H | 2017-02-08 |

If PostDate is DATE or DATETIME, instead of casting you could use DATEDIFF function to get the days between two dates and do the INT comparison:
WHERE DATEDIFF(DAY, PostDate, GETDATE())>2
If PostDate is varchar, stored in the format shown in the OP:
SET LANGUAGE british
SELECT ....
WHERE DATEDIFF(DAY, CAST(PostDate as datetime), GETDATE())>2
EDIT: Apparently DATEDIFF will work if PostDate is VARCHAR data type as well
DECLARE #PostDate VARCHAR(50)
SET #PostDate='08-01-2017'
SELECT DATEDIFF(DAY, #PostDate, GETDATE()) -- GETDATE() is 08-08-2017
-- Returns 7
Having said this, it is a good practice to keep Dates and Times as proper data types. In your case, you could change the data type to DATE, if possible. Will speed up lookups
EDIT 2: Please note, SQL Server works with ISO 8601 Date Format, which is YYYY-MM-DD, but the dates in OP's example, even though as per OP refer to dates in August 2017, are given incorrectly (referring to Jan and Feb 2017) and are stored as varchar. For correct results, these need to be either converted to DATE/DATETIME data type, or reformatted with the correct ISO format.
EDIT 3: Showing an example of casting OP's date format into proper, ISO format before calling DATEDIFF:
SET LANGUAGE british
DECLARE #PostDate VARCHAR(50)
SET #PostDate='2017-01-08'
SELECT DATEDIFF(DAY, CAST(#PostDate AS DATETIME), GETDATE()) -- GETDATE() is 08-08-2017
-- Returns 7
And the WHERE clause would be as follows:
-- In the begining of the select statement
SET LANGUAGE british
SELECT *
FROM ...
WHERE DATEDIFF(DAY, CAST(PostDate as datetime), GETDATE())>2

Is the POSTDATE - date column? If no then you are comparing strings and the result is as expected as '2017-01-08' > '08/10/2017' ('2' > '0'). Most probably you just need to cast the POSTDATE. See the example:
select
case
when '2017-01-08' > CONVERT(VARCHAR(10), dateadd(day,2,getdate()),101) THEN 1
ELSE 0
end without_cast,
case
when CAST('2017-01-08' AS DATE) > CONVERT(VARCHAR(10), dateadd(day,2,getdate()),101) THEN 1
ELSE 0
end with_cast
So what you need is:
SELECT *
FROM dbo.INVOICE i
WHERE MVT_TYPE = '122' AND SALESREF IS NOT NULL AND CAST(POSTDATE AS DATE) > CONVERT(VARCHAR(10), dateadd(day,2,getdate()),101)
AND NOT EXISTS (SELECT 1
FROM dbo.INVOICE
WHERE DEBIT = 'H' AND MVT_TYPE = '000' AND SALESREF = i.SALESREF )

Your problem is that you store a date as a varchar.
To compare 2 dates correctly you should compare their DATE rappresentation, not strings.
So I suggest you to convert your varchar to date, i.e. instead of
CAST(POSTDATE AS DATE) > CONVERT(VARCHAR(10), dateadd(day,2,getdate()),101)
you should use DATEFROMPARTS ( left(POSTDATE, 4), right(POSTDATE, 2), substring(POSTDATE,6,2)) > dateadd(day,2,cast(getdate() as date));.
DATEFROMPARTS function is available starting with SQL Server 2012, let me know if you are on the earlier version and I'll rewrite my code

Related

How to union with a distinct column and null rows

I am trying to get a list of persons status in SQL Server.
New records are added each day when a person clocks in, so rows do not exist for the current day, however there will be a record of that person clocking in at least one in the table history.
Example Data
| Name | Status | Date |
|------+--------+-------------------------|
| Joe | In | 2019-09-03 07:56:00.000 |
| Jack | In | 2019-09-03 06:52:00.000 |
| Joe | In | 2019-08-21 07:02:00.000 |
| Jack | In | 2019-08-14 06:09:00.000 |
| Jane | In | 2019-08-15 07:38:00.000 |
Example SQL
select name, status
from timeclock
where clockInTime >= dateAdd(day, 0, dateDiff(day, 0, current_timestamp))
union
select name, status
from timeclock
where not exists (?)
The first select will incorporate Joe and Jack since hey have clocked in in the last 24 hours.
Expected Output
| Name | Status |
|------+--------+
| Joe | In |
| Jack | In |
| Jane | Out |
How do I also select each member only once (distinct), but cast Jane's status as out?
This sounds like what you want more is something like this:
SELECT [Name],
CASE WHEN COUNT(CASE WHEN clockInTime >= dateAdd(day, 0, dateDiff(day, 0, current_timestamp)) THEN 1 END) > 0 THEN 'In' ELSE 'OUT' END AS [Status]
FROM timeclock
GROUP BY [Name];
This uses conditional aggregation, and only counts the rows after the time.

SQL Server: how min and max blank the other row if they same result and convert 24 hours to 12 hours without where method

I want the join EmpID to EmployeeNo and combine the last name, first name, and middle name from the second table and I want to the entries separate the O and I with min and max but if they don't have a min or max become blank or null I just want to become blank the certain row because if they don't blank the row the result is the same.
This is the 1st
| Entries | recordDate | Empid | Reference |
+-----------------------+-------------------------+--------+-----------+
| 0016930507201907:35I | 2019-05-07 00:00:00 000 | 001693 | 1693 |
| 0016930507201917:06O | 2019-05-07 00:00:00 000 | 001693 | 1693 |
| 0016930507201907:35I | 2019-05-08 00:00:00 000 | 001693 | 1693 |
| | 2019-05-08 00:00:00 000 | 001693 | 1693 |
2nd table
| LastName | FirstName | middleName | EmployeeNO |
+----------+-----------+------------+------------+
| Cruz | Kimberly | Castillo | 001693 |
I want to join that two table with the second table combine the lastname,FirstName, and middleName . the employeeNo join to Empid but the entries would be separate between I and O with min or max of certain empId but if the entries have not I or O it would be blank like this and also with where
| Name | EmployeeNO | RecordDate | TimeIn | TimeOut |
+-------------------------+------------+-------------------------+--------+---------+
| CRUZ, MA KIMBERLY, CA | 001693 | 2019-05-07 00:00:00 000 | 07:35 | 05:06 |
| CRUZ, MA KIMBERLY,CA | 001693 | 2019-05-08 00:00:00 000 | 07:35 |
If I have a where there have a error please help me this is the error
Conversion failed when converting date and/or time from character string.
The following will do it for you. I use a couple of nested CTEs. The first one doe your data conversions, the second one sets the min and max times, and the final query sets any rows that have missing I records to blank.
The test data uses table variables for your Table1 and Table2.
declare #E table(Entries varchar(50), RecordDate varchar(50), EmpID varchar(6), Ref int)
insert #e values ('0016930507201907:35I','2019-05-07 00:00:00 000','001693',1693)
,('0016930507201917:06O','2019-05-07 00:00:00 000','001693',1693)
,('0016930507201907:35I','2019-05-08 00:00:00 000','001693',1693)
,('','2019-05-08 00:00:00 000','001693',1693)
declare #B table(LastName varchar(50),FirstName varchar(50),middleName varchar(50),EmployeeNO varchar(6))
insert #B values ('Cruz','Kimberly','Castillo','001693')
;with e as (
select right(Entries,1) as InOut,
convert(datetime,left(recorddate,10)) as RecordDate, Entries,
substring(Entries,15,5) as t,
EmpID
from #e
where len(ltrim(Entries))>0
)
, details as (
select B.LastName + ',' + B.FirstName + ',' + B.MiddleName [Name],
EmployeeNO,
RecordDate,
min(case when InOut='I' then T else '99:99' end) as I,
max(case when InOut='O' then T else '' end) as O
from #B B
join e on e.EmpID=B.EmployeeNO
group by B.LastName,B.FirstName,B.MiddleName,EmployeeNO,RecordDate
)
select Name, EmployeeNO, RecordDate, case when I='99:99' then '' else I end as TimeIn, O as TimeOut
from details

How to add biweekly periods in a year to a table? (SQL Server)

This SQL Statement is not enough to add the right biweekly periods.
What i want is something like this:
Column 1 (period) / Column 2 (start period) / Column 3 (end period)
20160115 / 2016-01-01 / 2016-01-15
20160131 / 2016-01-15 / 2016-01-31
20160215 / 2016-02-01 / 2016-02-15
20160229 / 2016-02-16 / 2016-02-29
and so on...
This is what I have now, which is wrong / incomplete.
the value is being inserted correctly in the table columns but the gap is wrong since months don't have the same amount of days
Can someone help me? Thank you very much!
DECLARE #d date= '20020101'
WHILE #d<'20030101'
BEGIN
INSERT INTO [TABLE]
VALUES ((SELECT CONVERT(VARCHAR(8), #d, 112) AS [YYYYMMDD]), #d, #d, 'Fechado', '1')
SET #d=DATEADD(DAY,15,#d)
END
using a common table expression for an adhoc numbers table, and union all for two queries with some date math using dateadd():
declare #startdate date = '2016-01-01'
declare #enddate date = '2016-12-31'
;with cte as (
select top (datediff(month,#startdate,#enddate)+1)
Month = convert(date,dateadd(month,row_number() over (order by (select 1))-1,#startdate))
from master..spt_values
order by month
)
select
Period = convert(char(8), dateadd(day,14,month), 112)
, PeriodStart = month
, PeriodEnd = dateadd(day,14,month)
from cte
union all
select
Period = convert(char(8), eomonth(month), 112)
, PeriodStart = dateadd(day,14,month)
, PeriodEnd = eomonth(month)
from cte
order by period
rextester demo: http://rextester.com/BSYIXW7864
returns:
+----------+-------------+------------+
| Period | PeriodStart | PeriodEnd |
+----------+-------------+------------+
| 20160115 | 2016-01-01 | 2016-01-15 |
| 20160131 | 2016-01-15 | 2016-01-31 |
| 20160215 | 2016-02-01 | 2016-02-15 |
| 20160229 | 2016-02-15 | 2016-02-29 |
| 20160315 | 2016-03-01 | 2016-03-15 |
| 20160331 | 2016-03-15 | 2016-03-31 |
| 20160415 | 2016-04-01 | 2016-04-15 |
| 20160430 | 2016-04-15 | 2016-04-30 |
| 20160515 | 2016-05-01 | 2016-05-15 |
| 20160531 | 2016-05-15 | 2016-05-31 |
| 20160615 | 2016-06-01 | 2016-06-15 |
| 20160630 | 2016-06-15 | 2016-06-30 |
| 20160715 | 2016-07-01 | 2016-07-15 |
| 20160731 | 2016-07-15 | 2016-07-31 |
| 20160815 | 2016-08-01 | 2016-08-15 |
| 20160831 | 2016-08-15 | 2016-08-31 |
| 20160915 | 2016-09-01 | 2016-09-15 |
| 20160930 | 2016-09-15 | 2016-09-30 |
| 20161015 | 2016-10-01 | 2016-10-15 |
| 20161031 | 2016-10-15 | 2016-10-31 |
| 20161115 | 2016-11-01 | 2016-11-15 |
| 20161130 | 2016-11-15 | 2016-11-30 |
| 20161215 | 2016-12-01 | 2016-12-15 |
| 20161231 | 2016-12-15 | 2016-12-31 |
+----------+-------------+------------+
Create a function, then call in your query:
Function:
create FUNCTION advance_biweekly
(
#current date
)
RETURNS date
AS
BEGIN
--if it's the first day of month, advance two weeks
if (DAY(#current) = 1)
return DATEADD(WEEK, 2, #current)
else
--if its last day of month, advance one day
if (DAY(DATEADD(d, -1, DATEADD(m, DATEDIFF(m, 0, #current) + 1, 0))) = DAY(#current))
return DATEADD(DAY, 1, #current)
else
--else, it's the middle of the month, go to end
return dateadd(month,((YEAR(#current)-1900)*12)+MONTH(#current)-1,DAY(DATEADD(d, -1, DATEADD(m, DATEDIFF(m, 0, #current) + 1, 0)))-1)
return null
END
GO
code:
DECLARE #d date= '20020101'
WHILE #d<'20030101'
begin
set #d = dbo.advance_biweekly(#d)
print #d
end
result:
2002-01-15
2002-01-31
2002-02-01
2002-02-15
2002-02-28
2002-03-01
2002-03-15
2002-03-31
2002-04-01
2002-04-15
2002-04-30
2002-05-01

Declare new variable and input date based on rank

I want to 'clean' a dataset and declare a new variable, then input a date based on rank.
My dataset looks like this:
+-----+--------------+------------+-------+
| ID | Start_date | End_date | Rank |
+-----+--------------+------------+-------+
| a | May '16 | May '16 | 5 |
| a | Jun '16 | Jul '16 | 4 |
| a | Jul '16 | Aug '16 | 3 |
| a | Aug '16 | NULL '16 | 2 |
| a | Sept '16 | NULL '16 | 1 |
+-----+--------------+------------+-------+
I basically want to input the start date of rank 1 into the end date of rank 2 or say input start 5 into end 6 (always -1).
Have written the following to select into a tempory table and rank based on id and date:
SELECT
[Start_Date] as 'start'
,[End_Date] as 'end'
,[Code] as 'code'
,[ID] as 'id'
,rank() over (partition by [id] order by [Start_Date]) as 'rank'
INTO #1
FROM [Table]
ORDER BY [id]
Its the following part that doesn't work ...
DECLARE new_end
BEGIN select [#1].[start] into new_end FROM [#1]
WHERE (
([#1].[rank] = 1)
AND ([#1].[end] IS NULL)
)
Assuming you already have your dataset as provided in your question, this is just a simple self join, no?
declare #t table(ID nvarchar(1), Start_date date, End_date date, [Rank] int);
insert into #t values ('a','20170501','20170501',5),('a','20170601','20170701',4),('a','20170701','20170801',3),('a','20170801',NULL,2),('a','20170901',NULL,1);
select t1.ID
,t1.Start_date
,isnull(t1.End_date,t2.Start_date) as End_date
-- If you *always* want to overwrite the End_Date use this instead:
-- ,t2.Start_date as End_date
,t1.[Rank]
from #t t1
left join #t t2
on(t1.[Rank] = t2.[Rank]+1);
Output:
+----+------------+------------+------+
| ID | Start_date | End_date | Rank |
+----+------------+------------+------+
| a | 2017-05-01 | 2017-05-01 | 5 |
| a | 2017-06-01 | 2017-07-01 | 4 |
| a | 2017-07-01 | 2017-08-01 | 3 |
| a | 2017-08-01 | 2017-09-01 | 2 |
| a | 2017-09-01 | NULL | 1 |
+----+------------+------------+------+

MS SQL Query : Convert date and time output to different Format

I made a query to get all the data out of MS SQL that was put in the DB between 2 dates.
SELECT convert(varchar(30), [date], 113) as "Date"
FROM [I3_IC].[dbo].[Be]
Where [date] >= '2017-03-13T00:00:00.000' AND [date] <= '2017-03-19T00:00:00.000'
The output will be: 13 Mar 2017 10:40:13:017
And I want it to be: 13-03-2017 10:40
Any ideas how I can do that?
You can use FORMAT:
SELECT FORMAT(GETDATE(),'dd-MM-yyyy hh:mm') as [Date]
FROM [I3_IC].[dbo].[Be]
WHERE [date] >= '2017-03-13T00:00:00.000'
AND [date] <= '2017-03-19T00:00:00.000';
As an aside, you need to be careful with the conditions you use to select your date range (<= '2017-03-19T00:00:00.000' will actually select '2017-03-19T00:00:00.000', is that what you want?)
Prior to sql server 2012, concatenating two convert() styles
select convert(char(10),getdate(),105)+' '+convert(char(5),getdate(),108)
returns: 24-03-2017 16:05
In sql server 2012+ you can use format() as in the answer by Lamak.
select format(getdate(),'dd-MM-yyyy HH:mm')
returns: 24-03-2017 16:05
But format() can be slower, take a look here: format() is nice and all, but… - Aaron Bertand
In a quick test on my system, concatenating convert() was much faster than format() for this (except for when selecting top 1 ordered by unformatted value).
+---------+--------------------+------------------+-------------------+-----------------+----------------+----------------------------------------------------------------------------------------------------------------+
| func | total_elapsed_time | avg_elapsed_time | total_worker_time | avg_worker_time | total_clr_time | t |
+---------+--------------------+------------------+-------------------+-----------------+----------------+----------------------------------------------------------------------------------------------------------------+
| convert | 7000 | 1400.00 | 7000 | 1400.00 | 0 | DECLARE #d CHAR(10);SELECT #d = convert(char(10),d,105)+' '+convert(char(5),d,108) FROM dbo.dtTest ORDER BY d; |
| format | 135000 | 27000.00 | 135000 | 27000.00 | 128000 | DECLARE #d CHAR(10);SELECT #d = format(d,'dd-mm-yyyy hh:mm') FROM dbo.dtTest ORDER BY d; |
| convert | 14000 | 2800.00 | 14000 | 2800.00 | 0 | SELECT d = convert(char(10),d,105)+' '+convert(char(5),d,108) FROM dbo.dtTest ORDER BY d; |
| format | 143000 | 28600.00 | 143000 | 28600.00 | 123000 | SELECT d = format(d,'dd-mm-yyyy hh:mm') FROM dbo.dtTest ORDER BY d; |
| convert | 1000 | 200.00 | 1000 | 200.00 | 0 | SELECT TOP (1) convert(char(10),d,105)+' '+convert(char(5),d,108) FROM dbo.dtTest ORDER BY d; |
| format | 1000 | 200.00 | 1000 | 200.00 | 1000 | SELECT TOP (1) format(d,'dd-mm-yyyy hh:mm') FROM dbo.dtTest ORDER BY d; |
| convert | 4000 | 800.00 | 4000 | 800.00 | 0 | DECLARE #d CHAR(16);SELECT #d = convert(char(10),d,105)+' '+convert(char(5),d,108) FROM dbo.dtTest ORDER BY d; |
| format | 105000 | 21000.00 | 105000 | 21000.00 | 95000 | DECLARE #d CHAR(16);SELECT #d = format(d,'dd-mm-yyyy hh:mm') FROM dbo.dtTest ORDER BY d; |
+---------+--------------------+------------------+-------------------+-----------------+----------------+----------------------------------------------------------------------------------------------------------------+
The test is a modification of the test script included in the article by Aaron Bertrand. Modified version is on pastebin, here.

Resources