When IDs are identical check that the Ordinal is greater than the previous submission - sql

Example query
USE HES
SELECT T1.ID, T2.DATE, T1.ORDINAL
FROM TABLE1 AS T1
LEFT JOIN TABLE2 AS T2
ON T1.ID = T2.ID AND T1.PARTYEAR = T2.PARTYEAR
WHERE
T1.MONTHYEAR = '201501'
Results from example query
ID Date Ordinal
1 01/01/2016 1
1 02/01/2016 2
1 03/01/2016 3
2 04/01/2016 1
2 05/01/2016 2
3 06/01/2016 1
3 07/01/2016 2
3 08/01/2016 3
4 09/01/2016 1
4 10/01/2016 1
Question
Each user has a unique ID, for each ID how would I to check that each data submission contains an Ordinal that is greater than the one that was previously submitted.
So, in the example query results above, ID 4 contains an issue.
I'm fairly new to SQL, I've been searching for similar examples but with no success.
Any help would be greatly appreciated.

Use LAG with OVER clause:
WITH cte AS
(
SELECT T1.ID, T2.DATE, T1.ORDINAL, LAG(T1.ORDINAL) OVER(PARTITION BY T1.ID ORDER BY T1.ORDINAL) AS LagOrdinal
FROM TABLE1 AS T1
LEFT JOIN TABLE2 AS T2
ON T1.ID = T2.ID AND T1.PARTYEAR = T2.PARTYEAR
WHERE
T1.MONTHYEAR = '201501'
)
SELECT ID, DATE, ORDINAL, CASE WHEN ORDINAL > LagOrdinal THEN 1 ELSE 0 END AS OrdinalIsGreater
FROM cte;

Try this one:
SELECT * INTO #tmp
FROM (VALUES
(1, CONVERT(date, '01/01/2016'), 1),
(1, '02/01/2016', 2),
(1, '03/01/2016', 3),
(2, '04/01/2016', 1),
(2, '05/01/2016', 2),
(3, '06/01/2016', 1),
(3, '07/01/2016', 2),
(3, '08/01/2016', 3),
(4, '09/01/2016', 1),
(4, '10/01/2016', 1)
)T(ID, Date, Ordinal)
WITH Numbered AS
(
SELECT ROW_NUMBER() OVER (PARTITION BY ID ORDER BY Date) R, *
FROM #tmp
)
SELECT N2.ID, N2.Date, N1.Ordinal Prev, N2.Ordinal Curr
FROM Numbered N1
JOIN Numbered N2 ON N1.R+1=N2.R AND N1.ID=N2.ID
WHERE N1.Ordinal >= N2.Ordinal
It can be simplified when SQL Server version >= 2012, #tmp is your current result.

Like #Serg said, you can achieve this using lag
select *
from (
SELECT T1.ID, T2.DATE, T1.ORDINAL,
lag(t1.ordinal) over (partition by t1.id order by t2.date) as prevOrdinal
FROM TABLE1 AS T1
LEFT JOIN TABLE2 AS T2
ON T1.ID = T2.ID AND T1.PARTYEAR = T2.PARTYEAR
WHERE
T1.MONTHYEAR = '201501') as t
where t.prevOrdinal >= t.ordinal;
OUTPUT
ID DATE ORDINAL prevOrdinal
4 2016-10-01 1 1

Related

SQL Anywhere: find rows that are +-2 compared to another row

I have the following table:
ID User Form Depth
1 A ABC 2001
1 A XYZ 1001
1 B XYZ 1003
1 B DEF 3001
1 C XYZ 1000
If ID and Form are identical, I need to identify those rows that are +-2 from User A. Using the example above, the script would return:
ID User Form Depth
1 B XYZ 1003
1 C XYZ 1000
I already have a script which identifies rows with identical ID and Form--I just need the other part, but I'm struggling with figuring out the logic. I was hoping there was some kind of DIFF function I could use, but I can't find one for SQL Anywhere.
Does anyone have any suggestions?
Thanks!
If you're looking for the depth to be exactly +/-2 from A's depth:
select t1.*
from mytab t1,
mytab t2
where t1.id = t2.id
and t1.form = t2.form
and t1.user != 'A'
and t2.user = 'A'
and abs(t1.depth - t2.depth) = 2
go
ID User Form Depth
--- ----- ----- -----
1 B XYZ 1003
If you're looking for the depth to be within 2 of A's depth (ie, diff <= 2):
select t1.*
from mytab t1,
mytab t2
where t1.id = t2.id
and t1.form = t2.form
and t1.user != 'A'
and t2.user = 'A'
and abs(t1.depth - t2.depth) <= 2
go
ID User Form Depth
--- ----- ----- -----
1 B XYZ 1003
1 C XYZ 1000
This is pretty basic SQL so while this fiddle was done with MySQL, you should find the queries work in SQLAnywhere, too: sql fiddle
I think you want exists:
select t.*
from t
where t.user <> 'A' and
exists (select 1
from t t2
where t2.form = t.form and t2.id = t.id and
t2.depth between t.depth - 2 and t.depth + 2
);
A quick and dirty generalized method.
Replace #User with whomever you would like to remove.
DECLARE #table TABLE (
ID Int
,[User] VARCHAR(2)
,Form VARCHAR(3)
,Depth INT
)
DECLARE #User VARCHAR(2) = 'A'
INSERT INTO #table (ID , [User], Form, Depth)
VALUES
(1 , 'A' , 'ABC' , 2001),
(1 , 'A' , 'XYZ' , 1001),
(1 , 'B' , 'XYZ' , 1003),
(1 , 'B' , 'DEF' , 3001),
(1 , 'C' , 'XYZ' , 1000)
SELECT t1.ID, t1.[User], t1.Form, t1.Depth , ROW_NUMBER() OVER(ORDER BY t1.ID, t1.[User], t1.Form, t1.Depth) AS [row_number]
INTO #temp
FROM #table as t1
INNER JOIN (
SELECT t.ID, t.Form, COUNT('8') as [count]
FROM #table as t
GROUP BY ID, Form
HAVING COUNT('8') > 1
) as duplicates
ON duplicates.ID = t1.ID
AND duplicates. Form = t1.Form
ORDER BY ID, User, Form, Depth
-- SELECT * FROM #temp
SELECT [row_number] - 2 as value
INTO #range
FROM #temp as t
WHERE t.[User] = #User
--SELECT * FROM #range
INSERT INTO #range
SELECT [row_number] - 1
FROM #temp as t
WHERE t.[User] = #User
INSERT INTO #range
SELECT [row_number] + 1
FROM #temp as t
WHERE t.[User] = #User
INSERT INTO #range
SELECT [row_number] + 2
FROM #temp as t
WHERE t.[User] = #User
SELECT * FROM #temp
WHERE [row_number] IN (SELECT value FROM #range)
DROP TABLE #temp
DROP TABLE #range

SQL , self join

I have a set of data
name
AASADF2
AASADF3
ADSFFD2
ADSFFD3
AAWFWEF
SFASFSF
ADAQWEW
ASDAWFA
FSDGFRG
AFWEFR2
AFWEFR3
I wanted to retrieve data with name ending 2 or 3 also first 6 character should match
i.e
AASADF2
AASADF3
ADSFFD2
ADSFFD3
AFWEFR2
AFWEFR3
I was able to display the data vertically using self join
AASADF2 AASADF3
ADSFFD2 ADSFFD3
AFWEFR2 AFWEFR3
But I wanted that in horizontal format
AASADF2
AASADF3
ADSFFD2
ADSFFD3
AFWEFR2
AFWEFR3
Do we need to create temp table for this to acheive this format
Any thoughts?
Is this what you want :
select t.*
from table t
where right(nm, 1) in ('2', '3') and
exists (select 1 from table t1 where left(t1.nm, 6) = left(t.nm, 6))
One method uses window functions:
select t.*
from (select t.*, count(*) over (partition by left(t.field, 6)) as cnt
from t
) t
where cnt > 1 and field like '%[23]';
SELECT distinct t1.field FROM TABLE t1 JOIN TABLE t2
ON t1.field != t2.field
AND RIGHT(t1.field,1 ) IN ('2', '3')
AND LEFT(t1.field, 6) = LEFT(t2.field,6);
--*********************************************
here is a full example for you
DECLARE #table TABLE
(
field nvarchar(10)
)
insert #table (field) values ('AASADF2'),
('AASADF3'),
('ADSFFD2'),
('ADSFFD3'),
('AAWFWEF'),
('SFASFSF'),
('ADAQWEW'),
('ASDAWFA'),
('FSDGFRG'),
('AFWEFR2'),
('AFWEFR3');
SELECT distinct t1.field FROM #table t1 JOIN #table t2
ON t1.field != t2.field
AND RIGHT(t1.field,1 ) IN ('2', '3')
AND LEFT(t1.field, 6) = LEFT(t2.field,6)
output
field
----------
AASADF2
AASADF3
ADSFFD2
ADSFFD3
AFWEFR2
AFWEFR3
(6 row(s) affected)
I was able to retrieve partial results,
If we check count > 1 which will show the results which I am looking for , I used the below query
;with cte as
(select t1.name from t_name t1
where substring(t1.name, 8, 1) in
(
select substring(t2.name, 8, 1) from t_name t2
where
substring(t2.name, 8, 1)='2'
or substring(t2.name, 8, 1)='3'
)
)
B2018BT2 1
B2018BU2 1
B2018BV2 1
B2018BW2 1
B2018BX2 1
B2018BY2 1
B2018BZ2 1
B2020AA2 2
B2020AA3 2
B2020AB2 2
B2020AB3 2
B2020AC2 2
B2020AC3 2
Can someone suggest how to do a filer on count>1?

Oracle SQL reference outer table field in derived table nested query

I need to reference a field from the outer query in a derived table . The issue is that I need to limit the max date that is fetched from the derived table using a value from the outer table (A in this case) because the outer table is a temp work table that is populated with specific values by a process.
Below approach is incorrect as it cannot reference the outer table correctly. Is there a better way to write this?
Below is the example of how I want it to work:
SELECT A.EMP, X.SCHEDULE, A.DATE FROM CUST A, (SELECT T1.EMP, T1.NAME, CASE WHEN T1.USER1 = '3' THEN T2.USER4 ELSE T1.USER4 END AS Schedule
FROM TEMP1 T1, TEMP2 T2, TEMP3 T3
WHERE T1.EMP = T3.EMP
AND T2.VALUE= T3.VALUE
AND T1.DATE = (SELECT MAX(T1A.DATE) FROM TEMP1 T1A
WHERE T1A.EMP = T1.EMP
AND T1A.DATE <= A.DATE)
AND T2.DATE = (SELECT MAX(T2A.DATE) FROM TEMP2 T2A
WHERE T2A.VALUE= T2.VALUE
AND T2A.DATE <= A.DATE)
AND T3.DATE = (SELECT MAX(T3A.DATE) FROM TEMP3 T3A
WHERE T3A.EMP = T3.EMP
AND T3A.VALUE = T3.VALUE
AND T3A.DATE <= A.DATE)) X
WHERE A.EMP = X.EMP
AND X.EMP IN ('1','2');
Below is some sample data and results:
TABLE CUST
EMP DATE VALUE
1 1/1/17 R
2 2/1/17 R
TABLE TEMP1
EMP DATE USER1 USER4
1 3/2/16 3 4
1 5/1/17 3 3
2 2/1/17 9 2
TABLE TEMP2
DATE VALUE USER4
1/1/01 S 100
1/1/03 P 200
1/3/07 R 300
8/1/17 R 350
TABLE TEMP3
EMP DATE VALUE
1 3/2/16 R
1 5/1/17 R
2 2/1/17 R
The sample output should be:
EMP SCHEDULE DATE
1 300 1/1/17
2 2 2/1/17
I tried with your sample data and i got the output. I have rewritten your query and got the output expected.
SELECT A.emp, A.tdate,
CASE WHEN T1.USER1 = '3' THEN T2.USER4 ELSE T1.USER4 END AS Schedule
FROM CUST A, Temp1 t1, temp2 t2, temp3 t3
WHERE A.emp=t1.emp
AND A.tvalue = t2.tvalue
AND A.emp = t3.emp
AND A.tvalue = t3.tvalue
AND t1.tdate <=(SELECT max(tdate) from temp1 t where tdate<=A.tdate and t.emp=A.emp)
AND t2.tdate <= (SELECT max(tdate) from temp2 t where tdate <= A.tdate and A.tvalue = t.tvalue)
AND t3.tdate <=(SELECT max(tdate) from temp3 t where tdate <= A.tdate and A.tvalue = t.tvalue and t.emp=A.emp)
If you are going to have only one date in temp tables that is less than the date in cust table, then use the below query
SELECT A.emp, A.tdate,
CASE WHEN T1.USER1 = '3' THEN T2.USER4 ELSE T1.USER4 END AS Schedule
FROM CUST A, Temp1 t1, temp2 t2, temp3 t3
WHERE A.emp=t1.emp
AND A.tvalue = t2.tvalue
AND A.emp = t3.emp
AND A.tvalue = t3.tvalue
AND t1.tdate <=A.tdate
AND t2.tdate <= A.tdate
AND t3.tdate <= A.tdate
NOTE:- Column names like date and value willl throw error while table creation. They are reserved keywords
As "date" is a SQL reserved word (for a data type in Oracle) I would never use that as a column name. Below I have used DATECOL instead.
I think it is much easier to just compare the dates via a simple set of joins.
See this working here at SQL Fiddle
CREATE TABLE CUST
(EMP int, DATECOL date, VALUE varchar2(1))
;
INSERT ALL
INTO CUST (EMP, DATECOL, VALUE)
VALUES (1, to_date('01-Jan-2017','dd-mon-yyyy'), 'R')
INTO CUST (EMP, DATECOL, VALUE)
VALUES (2, to_date('01-Feb-2017','dd-mon-yyyy'), 'R')
SELECT * FROM dual
;
CREATE TABLE TEMP1
(EMP int, DATECOL date, USER1 int, USER4 int)
;
INSERT ALL
INTO TEMP1 (EMP, DATECOL, USER1, USER4)
VALUES (1, to_date('02-Mar-2016','dd-mon-yyyy'), 3, 4)
INTO TEMP1 (EMP, DATECOL, USER1, USER4)
VALUES (1, to_date('01-May-2017','dd-mon-yyyy'), 3, 3)
INTO TEMP1 (EMP, DATECOL, USER1, USER4)
VALUES (2, to_date('01-Feb-2017','dd-mon-yyyy'), 9, 2)
SELECT * FROM dual
;
CREATE TABLE TEMP2
(DATECOL date, VALUE varchar2(1), USER4 int)
;
INSERT ALL
INTO TEMP2 (DATECOL, VALUE, USER4)
VALUES (to_date('01-Jan-2001','dd-mon-yyyy'), 'S', 100)
INTO TEMP2 (DATECOL, VALUE, USER4)
VALUES (to_date('01-Jan-2003','dd-mon-yyyy'), 'P', 200)
INTO TEMP2 (DATECOL, VALUE, USER4)
VALUES (to_date('03-Jan-2007','dd-mon-yyyy'), 'R', 300)
INTO TEMP2 (DATECOL, VALUE, USER4)
VALUES (to_date('01-Aug-2017','dd-mon-yyyy'), 'R', 350)
SELECT * FROM dual
;
CREATE TABLE TEMP3
(EMP int, DATECOL date, VALUE varchar2(1))
;
INSERT ALL
INTO TEMP3 (EMP, DATECOL, VALUE)
VALUES (1, to_date('02-Mar-2016','dd-mon-yyyy'), 'R')
INTO TEMP3 (EMP, DATECOL, VALUE)
VALUES (1, to_date('01-May-2017','dd-mon-yyyy'), 'R')
INTO TEMP3 (EMP, DATECOL, VALUE)
VALUES (2, to_date('01-Feb-2017','dd-mon-yyyy'), 'R')
SELECT * FROM dual
;
Query 1:
SELECT
T1.EMP
--, T1.NAME
, CASE WHEN T1.USER1 = '3'
THEN
T2.USER4 ELSE
T1.USER4
END AS Schedule
, c.datecol
, t1.datecol t1date
, t2.datecol t2date
, t3.datecol t3date
FROM TEMP1 T1
INNER JOIN cust c ON T1.EMP = c.EMP
INNER JOIN TEMP3 T3 ON T1.EMP = T3.EMP
INNER JOIN TEMP2 T2 ON T3.VALUE = T2.VALUE
WHERE t1.datecol <= c.datecol
AND t2.datecol <= c.datecol
AND t3.datecol <= c.datecol
Results:
| EMP | SCHEDULE | DATECOL | T1DATE | T2DATE | T3DATE |
|-----|----------|----------------------|----------------------|----------------------|----------------------|
| 1 | 300 | 2017-01-01T00:00:00Z | 2016-03-02T00:00:00Z | 2007-01-03T00:00:00Z | 2016-03-02T00:00:00Z |
| 2 | 2 | 2017-02-01T00:00:00Z | 2017-02-01T00:00:00Z | 2007-01-03T00:00:00Z | 2017-02-01T00:00:00Z |
Also, over 25 years ago SQL standardized on a way better method of joining tables together. This simple trick to remember is stop using commas between table names in the FROM clause. This helps to ensure explicit join syntax is adopted.

add column values of two tables that have the same date

for example, I have 2 tables
resto1
day 1 = 1 2 3 4
resto2
day 1 = 5 6 7 8
I wanted to add the values of the first two columns that have the same date the result would be:
day_1_earned = 6 8 10 12
please help
assuming both the tables have a date column, let say named dt_col you can use below query to achieve the required result
select t1.column1+t2.column2 added_values from
table1 t1,table2 t2
where t1.dt_col = t2.dt_col;
SELECT T1.Column1 + T2.Column As TotalColumn
From T1, T2 WHERE T1.Date= T2.Date
SELECT Cr_date, SUM(Column1) Column1, SUM(Column2) Column2
FROM
(
SELECT Cr_Date,Column1,Column2 FROM #T1
UNION
SELECT Cr_Date,Column1,Column2 FROM #T2
) as res
GROUP BY Cr_Date

Calculate difference between rows with counter values in SQL

I got table T1 where I update some counters values
id, unix_time_stamp, counter1, counter10
1 , 1333435800 , 55 , 80
then i got table T2 where i copy those values
id, unix_time_stamp, counter1, counter10, value1, value10
1 , 1333435800 , 55 , 80 , 0 , 0
2 , 1333435801 , 60 , 87 , 5 , 7
3 , 1333435802 , 70 , 90 , 10 , 3
3 , 1333435804 , 80 , 100 , 5 , 5
this is done with some trigger function
INSERT INTO T2 (unix_time_stamp, counter1, counter10) SELECT unix_time_stamp, counter1, counter10 FROM T1 WHERE id=1
What i want is to calculate value1, value10 as a
(current_counter1 - last_counter1)/(current_time - last_time)
and put them in this insert.
for example value 1 with timestamp 1333435804 will be
value1=(80-70)/(1333435804-1333435802) = 5
other words
insert into t2
(unix_time_stamp, counter1, counter10, value1)
SELECT unix_time_stamp, counter1, counter10,
(counter1 - (select counter1 from T1 order by unix_time_stamp DESC LIMIT 1)/
(unix_time_stamp - (select unix_time_stamp from T1 order by unix_time_stamp DESC LIMIT 1)
FROM T1 WHERE id=1
but i want this in a little shorter version because i got 10 counters :)
Whole situation is little bit complicated, and i got some reason to not do this outside SQL
I am using sqlite
This is just to complicated to me :)
Please help.
Your question is a little unclear. Is this anywhere close?
DECLARE #Id int = 1
DECLARE #LastCounter1 int,
#LastCounter10 int,
#LastTime timestamp,
SELECT TOP 1 #LastCounter1 = counter1,
#LastCounter10 = counter10,
#LastTime = unix_time_stamp
FROM T2
WHERE id = #Id
ORDER BY unix_time_stamp DESC
INSERT INTO T2 (id, unix_time_stamp, counter1, counter10, value1, value10)
SELECT unix_time_stamp,
counter1,
counter10,
((counter1 - #LastCounter1) / (unix_time_stamp - #LastTime)),
((counter10 - #LastCounter10) / (unix_time_stamp - #LastTime))
Updated answer:
INSERT INTO T2 (id, unix_time_stamp, counter1, counter10, value1, value10)
SELECT T1.id,
T1.unix_time_stamp,
T1.counter1,
T1.counter10,
((T1.counter1 - [Last].counter1) / (T1.unix_time_stamp - [Last].unix_time_stamp)),
((T1.counter10 - [Last].counter10) / (T1.unix_time_stamp - [Last].unix_time_stamp))
FROM T1
INNER JOIN (
SELECT TOP 1 id,
counter1,
counter10,
unix_time_stamp
FROM T2
WHERE id = 1
ORDER BY unix_time_stamp DESC
) [Last] ON T1.id = [Last].id
WHERE T1.id = 1
I guess the following query calculates all data you need for your insert clause:
SELECT 1e0 * (cur.counter1 - prv.counter1)/(cur.unix_time_stamp - prv.unix_time_stamp) AS [value1]
, 1e0 * (cur.counter10 - prv.counter10)/(cur.unix_time_stamp - prv.unix_time_stamp) AS [value10]
, cur.counter1 AS [cur_counter1], cur.counter10 AS [cur_counter10], cur.unix_time_stamp AS [cur_time]
, prv.counter1 AS [prv_counter1], prv.counter10 AS [prv_counter10], prv.unix_time_stamp AS [prv_time]
FROM T1 cur, T1 prv
WHERE cur.counter1 = (SELECT MAX(aux_0.counter1) FROM T1 aux_0)
AND prv.counter1 = (SELECT MAX(aux_1.counter1) FROM T1 aux_1 WHERE aux_1.counter1 < cur.counter1);