Compare two rows in two columns - sql

I have a table for example like below
column1 column2
110 100
50 125
120 80
I want a selection in such a way that i will get something like this
column1 column2 difference
110 100 0
50 125 50
120 80 5
or just to be able to identify the difference between first row of column2 and second row of column1

You can do this with a LEFT JOIN:
SQL Fiddle
WITH Cte AS(
SELECT *,
rn = ROW_NUMBER() OVER(ORDER BY (SELECT NULL))
FROM tbl
)
SELECT
t1.*,
difference = ISNULL(t2.column2 - t1.column1, 0)
FROM cte t1
LEFT JOIN Cte t2
ON t1.rn = t2.rn + 1
Since there is no column to indicate the order, I added a ROW_NUMBER. Modify the ORDER BY clause to your preference.

Another way, could be this:
SELECT TB.COLUMN1,TB.COLUMN2,
(ISNULL(TB2.COLUMN2,TB.COLUMN1)-TB.COLUMN1) AS 'DIF'
FROM
(SELECT COLUMN1,COLUMN2,ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) - 1 AS 'R' FROM TEST ) TB
LEFT JOIN
(SELECT COLUMN1,COLUMN2,ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS 'R' FROM TEST ) TB2
ON TB.R = TB2.R
Until before the post I didn't know how dont let row_number 'order by' affected the query, but based on the above answer, now I Know it, using select null ;) thank you #Felix Pamittan

Related

How to select the top 3 values from a group based on date and exclude duplicate value?

If I three columns and 1 column has ID, 1 column has value and 1 column has date. Example, ID column has ID1, ID2, ID3. The value for each ID has a numeric value, say 1,2,3,4,5 for each ID.
How do I only get 3 results for each ID based on the most recent date descending.
I am using Sybase SQL. Is there any way I can write this?
I tried to use Row_number() and rank() but I don't get to use either of those functions with my SQL tool.
ID value Date
1 3 20190511
1 1 20190503
1 5 20190401
2 2 20190520
2 1 20190514
2 4 20190503
3 1 20190516
3 5 20190415
3 3 20190402
If you don't have row_number try this
SELECT *
FROM yourTable t1
WHERE (SELECT COUNT(*)
FROM yourTable t2
WHERE t1.id = t2.id
AND t1.date < t2.date) < 3
So if one id have 3 or more older rows wont appear.
with row_number
SELECT *
FROM (SELECT *, ROW_NUMBER() OVER (PARTITION BY id ORDER BY date DESC) as rn
FROM YourTable t1
) as t
WHERE t.rn <= 3
I assume you cant have multiple rows in same date. In that case you may want use RANK() or DENSE_RANK() and decide how handle ties.
One method uses a correlated subquery with in:
select t.*
from t
where t.date in (select top (3) t2.date
from t t2
where t2.id = t.id
order by t2.date desc
);
Note that this assumes that the dates are unique.

Fetching the next 3 or adjacent rows based upon a condition in postgreSQL

I have a database of more than 10,000 rows. eg:
id text
1 abc
2 ghj
3 cde
4 hif
5 klm
6 bbc
7 jkl
8 mno
9 dbo
10 ijk
I need to fetch the next three rows where the text matches a condition.
For eg: if I am doing a text like '%bc% query it should return me rows with ids 1,2,3,4,6,7,8,9 as row #1 and #6 is a match
Use below query to get the desired result. I am assuming you want to calculate next based on ID only and ID is always increment by 1, as you have mentioned in question.
If ID doesn't always increment by 1 , then first add a ROW Number and then replace id in t2 subquery and join condition with row number.
select t1.id, t1.id_text
from test t1
join
(
select id from test where id_text like '%bc%'
UNION
select id+1 from test where id_text like '%bc%'
UNION
select id+2 from test where id_text like '%bc%'
UNION
select id+3 from test where id_text like '%bc%'
) t2
on t1.id = t2.id;
SQL Fiddle Link
with -- Test data
t(i, x) as (values
(1,'abc'),(2,'ghj'),(3,'cde'),(4,'hif'),(5,'klm'),(6,'bbc'),(7,'jkl'),(8,'mno'),(9,'dbo'),(10,'ijk'))
select r.*
from
t as t0 cross join lateral (
select *
from t
where t.i >= t0.i
order by t.i
limit 4) as r
where t0.x like '%bc%'
order by r.i;
Lateral joins allows to use previous table in the next subquery.
You could use something like this:
SELECT next.*
FROM test, test next
WHERE test.text LIKE '%bc%'
AND (test.id + 1 = next.id OR test.id + 2 = next.id OR test.id + 3 = next.id)
I am not going to assume that the ids have no gaps. One method uses lag():
select t.*
from (select t.*,
lag(text) over (order by id) as prev_text,
lag(text, 2) over (order by id) as prev_text2,
lag(text, 3) over (order by id) as prev_text3
from t
) t
where text like '%bc%' or
prev_text like '%bc%' or
prev_text2 like '%bc%' or
prev_text3 like '%bc%';
You can also do this with one comparison, using other window functions:
select id, text
from (select t.*,
sum( (text like '%bc%')::int ) over (order by id rows between 3 preceding and current row) as cnt
from t
) t
where cnt > 0;
With an index on id, this might be the fastest approach to solving the problem.

"variable" cumulative value in SQL Server

I have a table that looks like this:
id count price
1 100 2
2 50 3
3 10 4
I want to get the price for certain cumulative count values, for instance:
when I "need" a count of 120, SQL cumulates the first x rows, and checks if the the cumulative value in the x'th row meets the requirements (>120) and then gives me back the price value of the x'th row.
For 120 I want to get 3 as price, for 159.5 then 4, for 80 a 2 etc.
Is that possible in SQL Server?
Think you want this:
select top 1
[price]
from(
select [id],
[count],
[price],
sum([count]) over(order by [id]) as run_sum
from tbl
) x
where 120 <= run_sum
order by run_sum
Fiddle: http://www.sqlfiddle.com/#!6/a1976/5/0
Fiddle example for 159.5, http://www.sqlfiddle.com/#!6/a1976/6/0
Fiddle example for 80, http://www.sqlfiddle.com/#!6/a1976/7/0
select top 1 price from
(
select t1.id, t1.count, SUM(t2.count) as sum, t1.price
from table_name t1
inner join table_name t2 on t1.id >= t2.id
group by t1.id, t1.count, t1.price
) t where t.sum >= 120 order by sum
fiddle

SQL get column value associated with max()

I have the following table
ID Version
--- ----------
123 1
124 2
125 3
126 4
127 5
128 6
Now I need to get ID value where version# is maximum
What I can do is
select ID from tbl where version = (select max(version) from tbl)
I don't want to use this since i need to use this part in a join inside another query and i don't want to complicate things further.
You can use select FIRST():
SELECT FIRST(id) FROM tbl ORDER BY Version DESC
Or limit the number results using LIMIT 1 option:
SELECT id FROM tbl ORDER BY Version DESC LIMIT 1
You mentioned you need this in a join, so something like this should do it
select *
from table_1 as t1
join (
select id,
row_number() over (order by version desc) as rn
from table_2
) as t2 on t1.id = t2.id and t2.rn = 1
(This is ANSI SQL as you didn't mention a DBMS - but should work on most modern DBMS)

SQL question: Getting records based on datediff from record to record

Ok, got a tricky one here... If my data looks like this:
Table1
ID Date_Created
1 1/1/2009
2 1/3/2009
3 1/5/2009
4 1/10/2009
5 1/15/2009
6 1/16/2009
How do I get the records that are 2 days apart from each other? My end result set should be rows 1-3, and 5-6. Thanks!
SELECT l.*
FROM Table1 l
INNER JOIN Table1 r ON DATEDIFF(d, l.Date_Created, r.Date_Created) = 2
AND r.Date_Created = (SELECT TOP 1 * FROM Table1 WHERE Date_Created > l.Date_Created ORDER BY Date_Create)
select distinct t1.*
from Table1 t1
inner join Table1 t2
on abs(cast(t1.Date_Created - t2.Date_Created as float)) between 1 and 2
-- what does this give you?
select DISTINCT t1.id, t1.date_created, t2.id, t2.date_created from table1 t1, table1 t2 where datediff(dd,t1.date_created,t2.date_created) = 2 AND t1.id != t2.id ORDER BY t1.id;
Would this work?
select t1.id, t2.id
from table1 t1
join table1 t2
on t2.date_created - t1.date_created <= 2
I might suggest using programming code to do it. You want to collect groups of rows (separate groups). I don't think you can solve this using a single query (which would give you just one set of rows back).
If you want to get the rows which are WITHIN 'N' days apart, you can try this:
select t1.date_created, t2.date_created
from table1 t1, table1 t2
where t1.id <> t2.id and
t2.date_created-t1.date_created between 0 and N;
for exmaple, as you said, if you want to get the rows which are WITHIN 2 days a part,
you can use the below:
select t1.date_created,t2.date_created
from table1 t1, table1.t2
where t1.id <> t2.id and
t2.date_created-t1.date_created between 0 and 2;
I hope this helps....
Regards,
Srikrishna.
A cursor will be fastest, but here is a SELECT query that will do it. Note that for "up to N" days apart instead of 2 you'll have to replace the table Two with a table of integers from 0 to N-1 (and the efficiency will get worse).
I'll admit it's not entirely clear what you want, but I'm guess you want the ranges of rows that contain at least two rows in all and within which the successive rows are at most 2 days apart. If dates increase along with IDs, this should work.
with Two as (
select 0 as offset union all select 1
), r2(ID, Date_Created_o, dr) as (
select
ID, Date_Created+offset,
Date_Created + offset - dense_rank() over (
order by Date_Created+offset
) from r cross join Two
)
select
min(ID) as start, max(ID) as finish
from r2
group by dr
having min(ID) < max(ID)
order by dr;