Set DateTime = To DateTime Of previous row on a view - sql

Below is the code to a view created to pull delimited values from a table into new rows in a view.
This works well, but I can't figure out how to set the TASDateTimeStart column value to the previous row's TASDateTimeEnd column value where the id in both rows are the same
SELECT
MallaghanApp.dbo.EmployeeClockIn.Id,
Employee,
JobType,
EmployeeName,
EmployeeNumber,
value AS SerialNumber,
CAST([TASDateTimeEnd] AS smalldatetime) [TASDateTimeEnd],
CAST([TASDateTimeStart] AS smalldatetime) [TASDateTimeStart],
ISNULL(((SELECT DATEDIFF(MINUTE, TASDateTimeStart, TASDateTimeEnd)) /
((SELECT LEN([SerialNo]) - LEN(REPLACE([SerialNo], ',', ''))) + 1)), (DATEDIFF(MINUTE, TASDateTimeStart, CURRENT_TIMESTAMP)) / ((SELECT LEN([SerialNo]) - LEN(REPLACE([SerialNo], ',', ''))) + 1)) AS MinutesClocked,
Department,
DepartmentNumber
FROM
EmployeeClockIn
CROSS APPLY
STRING_SPLIT([SerialNo], ',')
LEFT JOIN
EmployeeInfo ON Employee = EmployeeCombinedInfo
I keep getting an error
Subquery returned more than 1 value.This is not permitted when the subquery follows =,!=,<,<=,>,>= or when the subquery is used as an expression
Does anyone know the code needed here?

You can use the LAG function to get this value:
SELECT eci.Id,
eci.TASDateTimeEnd,
LAG(eci.TASDateTimeEnd) OVER (PARTITION BY eci.Id ORDER BY eci.TASDateTimeEnd) as PreviousTASDateTimeEnd
FROM EmployeeClockIn eci;

Related

SQL Server 2016 - Transpose column of integers to row by day

I need to transpose one of the columns in the data date to a row of string and group by 2 other columns. My sample data consists of the following data:
I need the result to look like this:
That is all the LNs in one row per Employee code, per day.
I tried the below code -
DECLARE #Process_Conditions_Loans VARCHAR(500)
SELECT
t1.EmplCode,
t1.LogDate,
#Process_Conditions_Loans = CONCAT(COALESCE(#Process_Conditions_Loans + ',', ''),PS2)
FROM
#temp t1
WHERE
LN IS NOT NULL
GROUP BY
EmplCode, LogDate
But I am getting an error
A SELECT statement that assigns a value to a variable must not be combined with data-retrieval operations.
I can not use group_concat since I am using SQL Server 2016.
Any help would be great appreciated.
Thanks,
JH
You can use the older form of string aggregation:
select emplcode, logdate,
stuff( (select concat(', ', ln)
from t
where t.emplcode = el.emplcode and t.logdate = el.logdate
order by ln
for xml path ('')
), 1, 2, ''
)
from (select distinct emplcode, logdate
from t
) el

Sort varchar with alpha numeric and special character values

I have a invoice_number field as varchar(20)
I have the select query as
SELECT Row_Number() OVER(ORDER BY case isnumeric(invoice_number)
when 1 then convert(bigint,invoice_num)
else 99999999999999999999
end) As id,
name,
submit_date,
invoice_number,
invoice_total,
currency_code
FROM vw_invoice_report
which works fine for a few scenarios but I couldn't make it work for all the the invoice_number values as below
f8ad2a28ddad4f6aa4df
0B849D69741145379079
20190313176617593442
ATOctober2000Promise
00100001010000000061
E285567EF0D0885E9160
SC1805000123000293
1999bernstyin2010
20600006307FFGMG
REVISED INVOICE F...
1111-2222(changzhou)
667339, 667340, 6...
18.12733562GAGA L...
IN-US01235055 ...
SSR-USD/426/2019 - 2
Nanny; Park Doug
184034
376840
376847-1
72692
72691
72690
72689
Am getting Error converting data type varchar to bigint. for some of the above data, can someone please help me make it work for the above test data?
Your problem is that some of your invoice numbers (for example 20190313176617593442) are too large for the BIGINT data type. You can work around this by keeping the values as strings, and left padding with 0 the numeric ones out to 20 digits for sorting. For example:
SELECT Row_Number() OVER(ORDER BY case isnumeric(invoice_number)
when 1 then REPLACE(STR(invoice_number, 20), ' ', '0')
else '99999999999999999999'
end) As id,
Demo (also showing converted invoice numbers) on SQLFiddle
Update
Based on OP comments and additional values to be sorted, this query should satisfy that requirement:
SELECT Row_Number() OVER(ORDER BY case
when isnumeric(invoice_number) = 1 then RIGHT('00000000000000000000' + REPLACE(invoice_number, '.', ''), 20)
when invoice_number like '%[0-9]-[0-9]%' and invoice_number not like '%[^0-9]' then REPLACE(STR(REPLACE(invoice_number, '-', '.'), 20), ' ', '0')
else '99999999999999999999'
end) As id,
invoice_number
FROM vw_invoice_report
Demo on SQLFiddle
Hmmm. I am thinking this might do what you want:
row_number() over (order by (case when isnumeric(invoicenumber) = 1
then len(invoicenumber)
else 99999
end
),
invoicenumber
)

SQL query to replace a string and convert rest of the string to Int

I have the below query. This query is supposed to look into my accounts table with my where parameter. That will return all account codes which start with leading 3 letters "DAG". The data return is consistently in the format leading with three letters followed by a number. I then want to get the most largest number. For that I order the string by converting it to Int.
I get this below error:
Msg 207, Level 16, State 1, Line 24
Invalid column name 'AccountCodeWithoutDAG '.
Here is my SQL query.
SELECT TOP 1
REPLACE(AccountCode, 'DAG', '') AS AccountCodeWithoutDAG
FROM
Accounts
WHERE
MangCode = 'ADFG'
ORDER BY
CONVERT(INT, AccountCodeWithoutDAG)
What am I doing wrong?
You can't use order by with convert alias
but you can try to use a subquery to get the AccountCodeWithoutDAG then order by it
SELECT TOP 1 AccountCodeWithoutDAG
FROM
(
SELECT REPLACE(AccountCode,'DAG','') AS AccountCodeWithoutDAG
FROM Accounts
where MangCode = 'ADFG'
) t1
Order by Convert(int, AccountCodeWithoutDAG )
As others have indicated, giving a calculated value an alias in the select does not give you the ability to use the same alias in subsequent clauses.
The way to do this nice-and-organized in sql server is cross apply:
SELECT TOP 1 AccountCodeWithoutDAG
FROM Accounts
cross apply (select REPLACE(AccountCode,'DAG','') AS AccountCodeWithoutDAG ) as calcquery
where
MangCode = 'ADFG'
Order by Convert(int, AccountCodeWithoutDAG )
The problem is the AccountCodeWithoutDAG, this is an alias and cannot be used in the order
Look what I did in testing below , use your replace statement in the convert part of the order by
declare #AccountCode varchar(100)='DAG123456'
SELECT top 1 REPLACE(#AccountCode,'DAG','') AS AccountCodeWithoutDAG
Order by Convert(int, REPLACE(#AccountCode,'DAG',''))

SUM and GROUP BY a substring in Splice (NoSql)

I am trying to to run a query like the one below. The goal is to get the total activity count for every user_key but because the user_key has a complex structure and I need only the part after the '|' symbol I had to use a substring function. However, when I'm trying to run the query, I get the
error:
SQL Error [42Y36]: Column reference 'USER_KEY' is invalid, or is part of an invalid expression. For a SELECT list with a GROUP BY, the columns and expressions being selected may only contain valid grouping expressions and valid aggregate expressions.
The substring function works OK outside this query. Any workarounds for this problem? Using Splice Machine (NoSql)
SELECT
substr(user_key, instr(user_key,'|') + 1) AS new_user_key,
SUM(
CAST(
activity_count AS INTEGER
)
) AS Total
FROM
schema_name.table_name
GROUP BY
substr(user_key, instr(user_key,'|') + 1)
Your GROUP BY column needs to match the SELECT
SELECT
substr(user_key, instr(user_key,'|') + 1) AS new_user_key,
SUM(
CAST(
activity_count AS INTEGER
)
) AS Total
FROM
schema_name.table_name
GROUP BY
substr(user_key, instr(user_key,'|') + 1) AS new_user_key
I found the answer myself. I used a table subquery:
SELECT new_table.new_user_key, sum(new_table.total)
from
(
SELECT
substr(user_key, instr(user_key,'|') + 1) AS new_user_key,
CAST(activity_count AS INTEGER) AS Total
FROM schema_name.table_name
)
as new_table
GROUP BY
new_table.new_user_key
Let's hope someone will find this post useful and will save some time to him or her.

How to find the average time difference between rows in a table?

I have a mysql database that stores some timestamps. Let's assume that all there is in the table is the ID and the timestamp. The timestamps might be duplicated.
I want to find the average time difference between consecutive rows that are not duplicates (timewise). Is there a way to do it in SQL?
If your table is t, and your timestamp column is ts, and you want the answer in seconds:
SELECT TIMESTAMPDIFF(SECOND, MIN(ts), MAX(ts) )
/
(COUNT(DISTINCT(ts)) -1)
FROM t
This will be miles quicker for large tables as it has no n-squared JOIN
This uses a cute mathematical trick which helps with this problem. Ignore the problem of duplicates for the moment. The average time difference between consecutive rows is the difference between the first timestamp and the last timestamp, divided by the number of rows -1.
Proof: The average distance between consecutive rows is the sum of the distance between consective rows, divided by the number of consecutive rows. But the sum of the difference between consecutive rows is just the distance between the first row and last row (assuming they are sorted by timestamp). And the number of consecutive rows is the total number of rows -1.
Then we just condition the timestamps to be distinct.
Are the ID's contiguous ?
You could do something like,
SELECT
a.ID
, b.ID
, a.Timestamp
, b.Timestamp
, b.timestamp - a.timestamp as Difference
FROM
MyTable a
JOIN MyTable b
ON a.ID = b.ID + 1 AND a.Timestamp <> b.Timestamp
That'll give you a list of time differences on each consecutive row pair...
Then you could wrap that up in an AVG grouping...
Here's one way:
select avg(timestampdiff(MINUTE,prev.datecol,cur.datecol))
from table cur
inner join table prev
on cur.id = prev.id + 1
and cur.datecol <> prev.datecol
The timestampdiff function allows you to choose between days, months, seconds, and so on.
If the id's are not consecutive, you can select the previous row by adding a rule that there are no other rows in between:
select avg(timestampdiff(MINUTE,prev.datecol,cur.datecol))
from table cur
inner join table prev
on prev.datecol < cur.datecol
and not exists (
select *
from table inbetween
where prev.datecol < inbetween.datecol
and inbetween.datecol < cur.datecol)
)
OLD POST but ....
Easies way is to use the Lag function and TIMESTAMPDIFF
SELECT
id,
TIMESTAMPDIFF('MINUTES', PREVIOUS_TIMESTAMP, TIMESTAMP) AS TIME_DIFF_IN_MINUTES
FROM (
SELECT
id,
TIMESTAMP,
LAG(TIMESTAMP, 1) OVER (ORDER BY TIMESTAMP) AS PREVIOUS_TIMESTAMP
FROM TABLE_NAME
)
Adapted for SQL Server from this discussion.
Essential columns used are:
cmis_load_date: A date/time stamp associated with each record.
extract_file: The full path to a file from which the record was loaded.
Comments:
There can be many records in each file. Records have to be grouped by the files loaded on the extract_file column. Intervals of days may pass between one file and the next being loaded. There is no reliable sequential value in any column, so the grouped rows are sorted by the minimum load date in each file group, and the ROW_NUMBER() function then serves as an ad hoc sequential value.
SELECT
AVG(DATEDIFF(day, t2.MinCMISLoadDate, t1.MinCMISLoadDate)) as ElapsedAvg
FROM
(
SELECT
ROW_NUMBER() OVER (ORDER BY MIN(cmis_load_date)) as RowNumber,
MIN(cmis_load_date) as MinCMISLoadDate,
CASE WHEN NOT CHARINDEX('\', extract_file) > 0 THEN '' ELSE RIGHT(extract_file, CHARINDEX('\', REVERSE(extract_file)) - 1) END as ExtractFile
FROM
TrafTabRecordsHistory
WHERE
court_id = 17
and
cmis_load_date >= '2019-09-01'
GROUP BY
CASE WHEN NOT CHARINDEX('\', extract_file) > 0 THEN '' ELSE RIGHT(extract_file, CHARINDEX('\', REVERSE(extract_file)) - 1) END
) t1
LEFT JOIN
(
SELECT
ROW_NUMBER() OVER (ORDER BY MIN(cmis_load_date)) as RowNumber,
MIN(cmis_load_date) as MinCMISLoadDate,
CASE WHEN NOT CHARINDEX('\', extract_file) > 0 THEN '' ELSE RIGHT(extract_file, CHARINDEX('\', REVERSE(extract_file)) - 1) END as ExtractFile
FROM
TrafTabRecordsHistory
WHERE
court_id = 17
and
cmis_load_date >= '2019-09-01'
GROUP BY
CASE WHEN NOT CHARINDEX('\', extract_file) > 0 THEN '' ELSE RIGHT(extract_file, CHARINDEX('\', REVERSE(extract_file)) - 1) END
) t2 on t2.RowNumber + 1 = t1.RowNumber