I have a table with 3 values.
ID AuditDateTime UpdateType
12 12-15-2015 18:09 1
45 12-04-2015 17:41 0
75 12-21-2015 04:26 0
12 12-17-2015 07:43 0
35 12-01-2015 05:36 1
45 12-15-2015 04:35 0
I'm trying to return only records where the UpdateType has changed from AuditDateTime based on the IDs. So in this example, ID 12 changes from the 12-15 entry to the 12-17 entry. I would want that record returned. There will be multiple instances of ID 12, and I need all records returned where an ID's UpdateType has changed from its previous entry. I tried adding a row_number but it didn't insert sequentially because the records are not in the table in order. I've done a ton of searching with no luck. Any help would be greatly appreciated.
By using a CTE it is possible to find the previous record based upon the order of the AuditDateTime
WITH CTEData AS
(SELECT ROW_NUMBER() OVER (PARTITION BY ID ORDER BY AuditDateTime) [ROWNUM], *
FROM #tmpTable)
SELECT A.ID, A.AuditDateTime, A.UpdateType
FROM CTEData A INNER JOIN CTEData B
ON (A.ROWNUM - 1) = B.ROWNUM AND
A.ID = B.ID
WHERE A.UpdateType <> B.UpdateType
The Inner Join back onto the CTE will give in one query both the current record (Table Alias A) and previous row (Table Alias B).
This should do what you're trying to do I believe
SELECT
T1.ID,
T1.AuditDateTime,
T1.UpdateType
FROM
dbo.My_Table T1
INNER JOIN dbo.My_Table T2 ON
T2.ID = T1.ID AND
T2.UpdateType <> T1.UpdateType AND
T2.AuditDateTime < T1.AuditDateTime
LEFT OUTER JOIN dbo.My_Table T3 ON
T3.ID = T1.ID AND
T3.AuditDateTime < T1.AuditDateTime AND
T3.AuditDateTime > T2.AuditDateTime
WHERE
T3.ID IS NULL
Alternatively:
SELECT
T1.ID,
T1.AuditDateTime,
T1.UpdateType
FROM
dbo.My_Table T1
INNER JOIN dbo.My_Table T2 ON
T2.ID = T1.ID AND
T2.UpdateType <> T1.UpdateType AND
T2.AuditDateTime < T1.AuditDateTime
WHERE
NOT EXISTS
(
SELECT *
FROM
dbo.My_Table T3
WHERE
T3.ID = T1.ID AND
T3.AuditDateTime < T1.AuditDateTime AND
T3.AuditDateTime > T2.AuditDateTime
)
The basic gist of both queries is that you're looking for rows where an earlier row had a different type and no other rows exist between the two rows (hence, they're sequential). Both queries are logically identical, but might have differing performance.
Also, these queries assume that no two rows will have identical audit times. If that's not the case then you'll need to define what you expect to get when that happens.
You can use the lag() window function to find the previous value for the same ID. Now you can pick only those rows that introduce a change:
select *
from (
select lag(UpdateType) over (
partition by ID
order by AuditDateTime) as prev_updatetype
, *
from YourTable
) sub
where prev_updatetype <> updatetype
Example at SQL Fiddle.
Related
I have a table having multiple negative and positive values, i want to delete only those number of records from table which are having negative values and have the same positive values . I'm not sure how to explain this scenario...
I will give a brief example-
I have a table with 6 records in which 2 records are with negative value and 4 record with positive
Name | number
A | 1
A |-1
A | 1
A |-1
A | 1
A | 1
So here i want to delete equal number of records of negative value and positive value
so my output should be
Name | Number
A | 1
A | 1
By using Row_number
;WITH CTE AS (
select *,ROW_NUMBER()OVER(PARTITION BY number ORDER BY (SELECT NULL)) -1 RN from Table1 )
Select Name, number from CTE WHERE RN NOT IN (1,0)
The following query assumes that your table has either a column called id which is either a primary key or some other means to order your records. Without any order, your question cannot be answered, and in fact the data sample you showed us would have no meaning, since internally records have no order in a SQL database.
WITH cte1 AS (
SELECT t1.id, t1.number, SUM(t2.number) as sum
FROM yourTable t1
INNER JOIN yourTable t2 on t1.id >= t2.id
GROUP BY t1.id, t1.number
)
WITH cte2 AS (
SELECT MAX(id) AS cutoff
FROM cte1
WHERE sum = 0
)
SELECT t.*
FROM yourTable t
WHERE t.id > (SELECT cutoff FROM cte2)
Note that I used the old school way of computing a running sum because you never told us the version of SQL Server which you are using. Hence, I didn't want to make assumptions about what you have available.
declare #negvalrecs int = (select COUNT(*) from tab where Number < 0)
delete
from tab
where Number < 0
delete top (#negvalrecs)
from tab
where Number > 0
Thanks for all your inputs!
I have a solution for it. We will be needing row number function for it.
--Providing row number to rows
select *,row_number () over (partition by name,number order by name) R into #1 from Table
--Taking negative values
select * into #2 from #1 where number<0
--Now Deleting those records from the main table by joining this table
delete #1 from #1 a inner join #2 b on a.name=b.name and a.number=b.number and a.r<=b.r
delete #1 from #1 a inner join #2 b on a.name=b.name and a.number=-(b.number) and a.r<=b.r
Hope it helps!
I recently encountered a similar problem and this is how I resolved it.
I also had records in table where there we no negatives for a given name the union all is to bring such records.
SELECT t1.name, t1.number
FROM table t1
LEFT OUTER JOIN
(SELECT name, number FROM table where number < 0) t2
ON
t1.name = t2.name and t1.number = t2.number
WHERE t1.number > 0 and t2.number IS NOT NULL
UNION ALL
SELECT t1.name, t1.number
FROM table t1
LEFT OUTER JOIN
(SELECT name, number FROM table where number < 0) t2
ON
t1.name = t2.name
WHERE t1.number > 0 and t2.number IS NULL;`
Try this,
delete from table_name
where substring(ltrim(rtrim(number)),1,1)='-'
I have two tables
Table 1 looks like this
ID Repeats
-----------
A 1
A 1
A 0
B 2
B 2
C 2
D 1
Table 2 looks like this
ID values
-----------
A 100
B 200
C 100
D 300
Using a view I need a result like this
ID values Repeats
-------------------
A 100 NA
B 200 2
C 100 2
D 300 1
that means, I want unique ID, its values and Repeats. Repeats value should display NA when there are multiple values against single ID and it should display the Repeats value in case there is single value for repeats.
Initially I needed to display the max value of repeats so I tried the following view
ALTER VIEW [dbo].[BookingView1]
AS
SELECT bv.*, bd2.Repeats FROM Table1 bv
JOIN
(
SELECT distinct bd.id, bd.Repeats FROM table2 bd
JOIN
(
SELECT Id, MAX(Repeats) AS MaxRepeatCount
FROM table2
GROUP BY Id
) bd1
ON bd.Id = bd1.Id
AND bd.Repeats = bd1.MaxRepeatCount
) bd2
ON bv.Id = bd2.Id;
and this returns the correct result but when trying to implement the CASE it fails to return unique ID results. Please help!!
One method uses outer apply:
select t2.*, t1.repeats
from table2 t2 outer apply
(select (case when max(repeats) = min(repeats) then max(repeats)
else 'NA'
end) as repeats
from table1 t1
where t1.id = t2.id
) t1;
Two notes:
This assumes that repeats is a string. If it is a number, you need to cast it to a string.
repeats is not null.
For the sake of completeness, I'm including another approach that will work if repeats is NULL. However, Gordon's answer has a much simpler query plan and should be preferred.
Option 1 (Works with NULLs):
SELECT
t1.ID, t2.[Values],
CASE
WHEN COUNT(*) > 1 THEN 'NA'
ELSE CAST(MAX(Repeats) AS VARCHAR(2))
END Repeats
FROM (
SELECT DISTINCT t1.ID, t1.Repeats
FROM #table1 t1
) t1
LEFT OUTER JOIN #table2 t2
ON t1.ID = t2.ID
GROUP BY t1.ID, t2.[Values]
Option 2 (does not contain explicit subqueries, but does not work with NULLs):
SELECT DISTINCT
t1.ID,
t2.[Values],
CASE
WHEN COUNT(t1.Repeats) OVER (PARTITION BY COUNT(DISTINCT t1.Repeats), t1.ID) > 1 THEN 'NA'
ELSE CAST(t1.Repeats AS VARCHAR(2))
END Repeats
FROM #table1 t1
LEFT OUTER JOIN #table2 t2
ON t1.ID = t2.ID
GROUP BY t1.ID, t2.[Values], t1.Repeats
NOTE:
This may not give desired results if table2 has different values for the same ID.
Few columns in my table looks like
Id Code date latest
1 T 2014-10-04 0
2 B 2014-10-19 0
2 B 2014-10-26 0
1 S 2014-10-05 0
1 T 2014-10-06 0
1 T 2014-10-08 1
2 P 2014-10-27 1
I am tracking all changes made by each ID. if there is any change, I insert new row and update the latest value column.
What I want is for each Id, I should be able to find last code where latest is 0. Also, that code should not be equal to existing code(latest = 1) So for id = 1, answer cannot be
Id Code
1 T
as for id = 1 T is existing code (latest = 1).
So ideally my output should look like:
Id Code
1 S
2 B
I think I can get the latest value for code for each id where latest = 0.
But how do I make sure that it should not be equal to existing code value (latest = 1)
Works in Postgres:
SELECT DISTINCT ON (t0.id)
t0.id, t0.code
FROM tbl t0
LEFT JOIN tbl t1 ON t1.code = t0.code
AND t1.id = t0.id
AND t1.latest = 1
WHERE t0.latest = 0
AND t1.code IS NULL
ORDER BY t0.id, t0.date DESC;
I use the combination of a LEFT JOIN / IS NULL to remove siblings of rows with latest = 1. There are various ways to do this:
Select rows which are not present in other table
Details for DISTINCT ON:
Select first row in each GROUP BY group?
Version with CTE and 2x LEFT JOIN
Since Redshift does not seem to support DISTINCT ON:
WITH cte AS (
SELECT t0.*
FROM tbl t0
LEFT JOIN tbl t1 ON t1.code = t0.code
AND t1.id = t0.id
AND t1.latest = 1
WHERE t0.latest = 0
AND t1.id IS NULL
)
SELECT c0.id, c0.code
FROM cte c0
LEFT JOIN cte c1 ON c1.id = c0.id
AND c1.date > c0.date
WHERE c1.id IS NULL
ORDER BY c0.id;
SQL Fiddle showing both.
I think the following does what you want:
select t.*
from (select distinct on (code) id, code
from table t
where latest = 0
order by code, date desc
) t
where not exists (select 1 from table t2 where t2.id = t.id and t2.code = t.code and t2.latest = 1);
I believe you should have a data for the current version and you should create another table where you would store previous revisions, having foreign key to the Id. Your Id does not fulfill the general expectations for a column with such a name. So, ideally, you would:
create a table Revisions(Id, myTableId, core, date, revision), where Id would be auto_increment primary key and myTableId would point to the Id of the records (1 and 2 in the example)
migrate the elements into revision: insert into Revisions(myTableId, core, date, revision) select Id, core, date latest from MyTable where latest = 0
update the migrated records: update Revisions r1 set r1.revision = (select count(*) from revisions r2 where r2.date < r1.date)
remove the old data from your new table: delete from MyTable where latest = 0
drop your latest column from MyTable
From here, you will be always able to select the penultimate version, or second to last and so on, without problems. Note, that my code suggestions might be of wrong syntax in postgreSQL, as I have never used it, but the idea should work there as well.
There is a table with Columns as below:
Id : long autoincrement;
timestamp:long;
price:long
Timestamp is given as a unix_time in ms.
Question: what is the average time difference between the records ?
First thought is a sub-query grabbing the record immediately previous:
SELECT timestamp -
(select top 1 timestamp from Table T1 where T1.Id < Table.Id order by Id desc)
FROM Table
Then you can take the average of that:
SELECT AVG(delta)
from (SELECT timestamp -
(select top 1 timestamp from Table T1 where T1.Id < Table.Id order by Id desc) as delta
FROM Table) T
There will probably need to be some handling of the null that results for the first row, but I haven't tested to be sure.
In SQL Server, you could write something like that to get that information:
SELECT
t1.ID, t2.ID,
DATEDIFF(MILLISECOND, t2.PriceTime, test2.PriceTime)
FROM table t1
INNER JOIN table t2 ON t2.ID = t1.ID-1
WHERE t1.ID > (SELECT MIN(ID) FROM table)
and if you're only interested in the AVG across all entries, you could use:
SELECT
AVG(DATEDIFF(MILLISECOND, t2.PriceTime, test2.PriceTime))
FROM table t1
INNER JOIN table t2 ON t2.ID = t1.ID-1
WHERE t1.ID > (SELECT MIN(ID) FROM table)
Basically, you need to join the table with itself, and use "t1.ID = t2.ID-1" to associate item no. 2 in one table with item no. 1 in the other table and then calculate the time difference between the two. In order to avoid accessing item no. 0 which doesn't exist, use the "T1.ID > (SELECT MIN(ID) FROM table)" clause to start from the second item.
Marc
At a guess:
SELECT AVG(timestamp)
I think you need to provide more information in your question for us to help.
If you mean difference between each-other row:
select AVG(x) from (
select a.timestamp - b.timestamp as x
from table a, table b -- this multiplies a*b ) sub
SELECT AVG(T2.Timestamp - T1.TimeStamp)
FROM Table T1
JOIN Table T2 ON T2.ID = T1.ID + 1
try this
Select Avg(E.Timestamp - B.Timestamp)
From Table B Join Table E
On E.Timestamp =
(Select Max(Timestamp)
From Table
Where Timestamp < R.Timestamp)
I've got a query joining several tables and returning quite a few columns.
An indexed column of another table references the PK of one of these joined tables. Now I would like to add another column to the query that states if at least one row with that ID exists in the new table.
So if I have one of the old tables
ID
1
2
3
and the new table
REF_ID
1
1
1
3
then I'd like to get
ID REF_EXISTS
1 1
2 0
3 1
I can think of several ways to do that, but what is the most elegant/efficient one?
EDIT
I tested the performance of the queries provided with 50.000 records in the old table, every other record matched by two rows in the new table, so half of the records have REF_EXISTS=1.
I'm adding average results as comments to the answers in case anyone is interested. Thanks everyone!
Another option:
select O.ID
, case when N.ref_id is not null then 1 else 0 end as ref_exists
from old_table o
left outer join (select distinct ref_id from new_table) N
on O.id = N.ref_id
I would:
select distinct ID,
case when exists (select 1 from REF_TABLE where ID_TABLE.ID = REF_TABLE.REF_ID)
then 1 else 0 end
from ID_TABLE
Provided you have indexes on the PK and FK you will get away with a table scan and index lookups.
Regards
K
Use:
SELECT DISTINCT t1.id,
CASE WHEN t2.ref_id IS NULL THEN 0 ELSE 1 END AS REF_EXISTS
FROM TABLE_1 t1
LEFT JOIN TABLE_2 t2 ON t2.ref_id = t1.id
Added DISTINCT to ensure only unique rows are displayed.
A join could return multiple rows for one id, as it does for id=1 in the example data. You can limit it to one row per id with a group by:
SELECT
t1.id
, COUNT(DISTINCT t2.ref_id) as REF_EXISTS
FROM TABLE_1 t1
LEFT JOIN TABLE_2 t2 ON t2.ref_id = t1.id
GROUP BY t1.id
The group by ensures there's only one row per id. And count(distinct t2.ref_id) will be 1 if a row is found and 0 otherwise.
EDIT: You can rewrite it without a group by, but I doubt that will make things easer:
SELECT
t1.id
, CASE WHEN EXISTS (
SELECT * FROM TABLE_2 t2 WHERE t2.ref_id = t1.id)
THEN 1 ELSE 0 END as REF_EXISTS
, ....
FROM TABLE_1 t1