How could I get consecutive pairs in a column with duplication? - sql

I'm struggling to find a way to get consecutive pairs in a column of a table but failed so far. To explain more specifically, let me give an example.
Let's say I have a table (t1) with 1 column (col1) of char as follows:
col1
------
a
a
a
c
e
a
g
i
What I want to get is consecutive pairs like
(a,a) (a,a) (a,c) (c,e) (e,a) (a g) (g,i).
For example, building a new table(t2) which has a column(col2) of string as follows:
col2
------
a,a
a,a
a,c
c,e
e,a
a,g
g,i
(if I can exclude the pairs of same value (e.g. (a,a)), it would be better)
Please assume that there is no analytic function like lag() and unable to use pl/sql.
I have to solve this problem with basic sql statements.
I have a quite simple sql query to solve this kind of problem as follow:
select t1.col as col1, min(t2.col) as col2
from table1 t1 inner join table1 t2 on t2.col > t1.col
group by t1.col
order by t1.col
But this query is working when we assume there is no duplication.
Please help. Any comment or idea would be very thankful.

You can use a ROW_NUMBER to achieve this:
SELECT a.col,b.col
FROM (SELECT col,ROW_NUMBER () OVER (ORDER BY (SELECT 1)) 'Row_Rank'
FROM Table1
)a
LEFT JOIN (SELECT col,ROW_NUMBER () OVER (ORDER BY (SELECT 1)) 'Row_Rank'
FROM Table1
)b
ON a.Row_Rank = b.Row_Rank - 1
Ideally you'd have something that you could segment this by, as it's nice to ensure ordering, but the above works. The ROW_NUMBER just creates a number for each row, which you can use to self-join with an offset of 1 row.
EDIT: Changed to LEFT JOIN, depending on what you want to return for the last record you can use LEFT or INNER, and you could wrap the selected columns in ISNULL if you wanted to return a specific value to be paired with the last value that has no next row.

Related

SQL: How to join two rows on the same table, based on the same timestamp?

This is a data output table in postgreSQL.
I am looking for a way to join rows 7 and 8 on the timestamp condition to remove the [null] values.
This is the expected output I am looking for.
So that I will only have one row when the timestamp matches.
--------------------------------------------------------
05:32:33 | Pump2Stop | 49
--------------------------------------------------------
Any idea how can this be done?
assuming you only ever have one record per timestamp with a non-null value in a specific column, try this:
SELECT to_timestamp, MAX(str_v), MAX(long_v)
FROM table
GROUP BY to_timestamp;
You can use a self-join
select stp.to_timestamp,
stp.str_v,
strt.long_v
from the_table stp
join the_table strt
on stp.to_timestamp = strt.to_timestamp
and strt.str_v is null
where stp.str_v = 'Pump2Stop'
If I understand correctly, you want all rows -- with the specified rows combined. Based on your comment, I think this does what you want:
select t1.to_timestamp, t1.str_v
coalesce(t2.long_v, t1.long_v)
from t t1 left join
t t2
on t.to_timestamp = t2.to_timestamp and
t.str_v in ('Pump2Stop', 'Pump2Stop')
where t1.str_v is not null;
You can also use a window function:
select t.to_timestamp, t.str_v,
coalesce(long_v, imputed_long_v) as long_v
from (select t.*,
max(long_v) over (partition by to_timestamp) as imputed_long_v
from t
) t
where str_v is not null;

Subquery with 2 parameters in SQL

I have a table in SQL which looks like this:
[
Now, I want the resultant table based on 2 conditions:
Prev_trans_id should match the transactions_ID
Only those entries should come where mod of Amount value is not equal.
The resultant table should like this:
SO, in the resultant table, I dont want row with Transcation_ID as 104 since the mod of amount is same. $1 was paid and $1 was refunded.
I was able to do 1st part of it but not able to do 2nd part as I am new to SQL. This is my code for the 1st part:
select * from sample_table
where prev_trans_id in
(select transaction_id from sample_table)
If I can get the 2nd condition also incorporated in the same query, it would be very helpful.
Use a JOIN, not IN
SELECT t1.*
FROM sample_table AS t1
JOIN sample_table AS t2
ON t1.prev_trans_id = t2.transaction_id AND t1.amount != -1 * t2.amount
BTW, it's not mod of the amounts, it's the negation of the amounts that you want to compare.
There's no need to use a subquery here as it can be achieved with a basic select ... from ... where ... query on table1 and table2. Please see the query below:
select table2.*
from sample_table table1, sample_table table2
where table1.transaction_id = table2.prev_trans_id
and (table1.amount - table2.amount) <> 0

Order by data as per supplied Id in sql

Query:
SELECT *
FROM [MemberBackup].[dbo].[OriginalBackup]
where ration_card_id in
(
1247881,174772,
808454,2326154
)
Right now the data is ordered by the auto id or whatever clause I'm passing in order by.
But I want the data to come in sequential format as per id's I have passed
Expected Output:
All Data for 1247881
All Data for 174772
All Data for 808454
All Data for 2326154
Note:
Number of Id's to be passed will 300 000
One option would be to create a CTE containing the ration_card_id values and the orders which you are imposing, and the join to this table:
WITH cte AS (
SELECT 1247881 AS ration_card_id, 1 AS position
UNION ALL
SELECT 174772, 2
UNION ALL
SELECT 808454, 3
UNION ALL
SELECT 2326154, 4
)
SELECT t1.*
FROM [MemberBackup].[dbo].[OriginalBackup] t1
INNER JOIN cte t2
ON t1.ration_card_id = t2.ration_card_id
ORDER BY t2.position DESC
Edit:
If you have many IDs, then neither the answer above nor the answer given using a CASE expression will suffice. In this case, your best bet would be to load the list of IDs into a table, containing an auto increment ID column. Then, each number would be labelled with a position as its record is being loaded into your database. After this, you can join as I have done above.
If the desired order does not reflect a sequential ordering of some preexisting data, you will have to specify the ordering yourself. One way to do this is with a case statement:
SELECT *
FROM [MemberBackup].[dbo].[OriginalBackup]
where ration_card_id in
(
1247881,174772,
808454,2326154
)
ORDER BY CASE ration_card_id
WHEN 1247881 THEN 0
WHEN 174772 THEN 1
WHEN 808454 THEN 2
WHEN 2326154 THEN 3
END
Stating the obvious but note that this ordering most likely is not represented by any indexes, and will therefore not be indexed.
Insert your ration_card_id's in #temp table with one identity column.
Re-write your sql query as:
SELECT a.*
FROM [MemberBackup].[dbo].[OriginalBackup] a
JOIN #temps b
on a.ration_card_id = b.ration_card_id
order by b.id

Subtracting two SELECT statements to produce a single result?

I did find this post: how do I subtract values from two select statements
but I cannot get it to work on SQL-Server 2008.
I have two basic SELECT statements. No GROUP BYs or anything, just a SELECT, FROM and WHERE.
I would like to subtract one of these SELECTs from the other. Both return one row and column.
Could somebody please help?
SELECT
((SELECT Field1 AS BaseBase FROM Table1
WHERE Field2 = 'something'
AND Field3 =
(SELECT Field4 FROM SEL_Function('something','something')) as tab1)
-
((SELECT Field5 FROM SEL_Function('something','something') as tab2)
Alright I don't think the cross apply is necessary fiddle won't allow functions but check out this fiddle. You will want to swap out the table of the same name with the function. I think you were getting bit by a combination of alias and paren mismatches.
http://sqlfiddle.com/#!3/dc72f/13
In the provided link there is
select v1.Value1 - v2.Value2 from ((bla) AS v1 CROSS JOIN (blubb) AS v2
which I would translate to this (in your case)
SELECT tab1.BaseBase - tab2.Minus FROM
((SELECT Field1 AS BaseBase FROM Table1
WHERE Field2 = 'something'
AND Field3 =
(SELECT Field4 FROM SEL_Function('something','something')) as tab1
CROSS JOIN
(SELECT Field5 AS Minus FROM SEL_Function('something','something')) as tab2)
Like stated in the comments, it would be advisable to specify the affected table. However, I think that it might work (I have no testing system at hand :( ) without those explicit definitions as well, because the names are unique.

SQL First Match or just First

Because this is part of a larger SQL SELECT statement, I want a wholly SQL query that selects the first item matching a criteria or, if no items match the criteria, just the first item.
I.e. using Linq I want:
Dim t1 = From t In Tt
Dim t2 = From t In t1 Where Criteria(t)
Dim firstIfAny = From t In If(t2.Any, t2, t1) Take 1 Select t
Because If is not part of Linq, LinqPad doesn't show a single SQL statement, but two, the second depending upon whether the Criteria matches any of the Tt values.
I know it will be SELECT TOP 1 etc. and I can add ORDER BY clauses to get the specific first one I want, but I'm having trouble thinking of the most straightforward way to get the first of two criteria. (It was at exactly this point when I was able to solve this myself.)
Seeing as I don't see an existing question for this, I will let it stand. I'm sure someone else will see the answer quickly.
select top 1 *
from (
select top 1 *, 1 as Rank from MyTable where SomeColumn = MyCriteria
union all
select top 1 *, 2 as Rank from MyTable order by MyOrderColumn
) a
order by Rank
I've gone with this:
SELECT TOP 1 *
FROM MyTable
WHERE SomeColumn = MyCriteria
OR NOT (EXISTS (SELECT NULL FROM MyTable WHERE SomeColumn = MyCriteria))
ORDER BY MyOrdering
My actual SomeColumn = MyCriteria is rather more complex of course, as well as other unrelated where clauses.