Subquery with 2 parameters in SQL - 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

Related

find rows with specific condition on the columns

I'm trying to make an sql query where I will compare all rows to each other and check their columns. using condition I will display the need rows.
I made this one and I got an error message "UNKNOWN COLUMN" , any ideas how to fix it?
select * from table1 as tb1 where DATE_FORMAT(start_datetime, '%H:%i:%s') in (
select DATE_FORMAT(start_datetime, '%H:%i:%s') from table1 as tb2
group by DATE_FORMAT(start_datetime, '%H:%i:%s') having count(*) > 2 AND (end_datetime = start_datetime) OR (tb1.code = tb2.code)
) AND user_id = 1
to explain what I wanna do,I'm searching the rows that have the same time then if the start_datetime is equal to the end_datetime or the rows have the same code
in this
example my sql query should return the last 4 rows, 2 because they have the same start_datetime and end_datetime and 2 because the have the same time on the start_datetime , the same code and the end_datetime is NULL
you can use "union all" operator between 2 data sets
select code,start_datetime,end_datetime from table2
where start_datetime=end_datetime
union all
select t2.code,t2.start_datetime,t2.end_datetime from
(
select code,start_datetime,end_datetime from table2
where start_datetime!=end_datetime
and end_datetime is null
) as t2
inner join ( select code table2 group by DATE_FORMAT(start_datetime, '%H:%i:%s'),code having count(*)>=2 )as t1 on t2.code=t1.code
Notwithstanding any errors we can't see because you haven't provided the schema, "fs1.code" and "fs2.code" do not seem to reference table aliases that have been defined in the statement. The aliases that do get defined are "tb1" and "tb2" and they don't match.

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

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

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.

SQL query - Selecting distinct values from a table

I have a table in which i have multiple entries against a FK. I want to find out the value of FK which do not have certain entries e.g
my table has following entries.
PK----------------FK-----------------Column entries
1----------------100-----------------ab1
2----------------100-----------------ab2
3----------------100-----------------ab4
4----------------200-----------------ab1
5----------------200-----------------ab2
6----------------200-----------------ab3
7----------------300-----------------ab1
8----------------300-----------------ab2
9----------------300-----------------ab3
10---------------300-----------------ab4
Now, from this table i want to filter all those FK which do not have ab3 or ab4 in them. Certainly, i expect distinct values i.e. in this case result would be FK= 100 and 200.
The query which i am using is
select distinct(FK)
from table1
where column_entries != 'ab3'
or column_entries != 'ab4';
Certainly, this query is not fetching the desired result.
try the following :-
select distinct fk_col from table1
minus
(select distinct fk_col from table1 where col_entry='ab3'
intersect
select distinct fk_col from table1 where col_entry='ab4')
This will show all those FKs which do not have 'ab3' and 'ab4'. i.e. 100 and 200 in your case
The below script may be the solution if I got your question in a right way.
SELECT DISTINCT(TableForeignKey)
FROM Test
WHERE TableForeignKey NOT IN (
SELECT T1.TableForeignKey
FROM Test T1 INNER JOIN Test T2 ON T1.TableForeignKey = T2.TableForeignKey
WHERE T1.TableEntry = 'ab3' AND T2.TableEntry = 'ab4')
SQLFiddle Demo
You could use GROUP BY with conditional aggregation in HAVING:
SELECT FK
FROM table1
GROUP BY FK
HAVING COUNT(CASE column_entries WHEN 'ab3' THEN 1 END) = 0
OR COUNT(CASE column_entries WHEN 'ab4' THEN 1 END) = 0
;
The two conditional aggregates count 'ab3' and 'ab4' entries separately. If both end up with results greater than 0, then the corresponding FK has both 'ab3' and 'ab4' and is thus not returned. If at least one of the counts evaluates to 0, then FK is considered satisfying the requirements.

update a field based on subtotal from another table

I'm using oracle(10).
I've got two tables as follows:
Table1 (uniq rows):
ID AMOUNT DATE
Table2:
ID AMOUNT1 AMOUNT2 ...AMOUNTN DATE
Table2 is connected many to one to Table1 connected via ID.
What I need is update-ing Table1.DATE with: the last (earliest) date from Table2 where Table1.AMOUNT - SUM(Table2.AMOUNT1) <= 0, when reading table 2 backwards by the Table2.DATE field.
Is there a simple way to do it?
Thanks in advance!
UPDATE: as I see from your answers I had misspecified the question a bit. So here goes a detailed example:
Table1 has:
ID: 1 AMOUNT:100 DATE:NULL
Table2 has (for ID: 1 so ID is not listed in here):
AMOUNT1 DATE
50 20080131
30 20080121
25 20080111
20 20080101
So in this case I need 20080111 as the DATE in Table1 as 50+30+25 => 100.
Based on your revised question, this is a case for using analytic functions.
Assuming you meant >=100 rather than <= 100 as your example implies, and renaming columns DATE to THEDATE since DATE is a reserved word in Oracle:
update table1 set thedate=
( select max(thedate) from
( select id, thedate,
sum(amount1) over (partition by id order by thedate desc) cumsum
from table2
) v
where v.cumsum >= 100
and v.id = table1.id
)
If the 100 means the current value of table1 then change that line to:
where v.cumsum >= table1.amount
First off - your database layout feels severely wrong, but I guess you can't / don't want to change it. Table1 should probably be a view, and Table2 does not make the impression of proper normalization. Something like (ID, AMOUNT_TYPE, AMOUNT_VALUE, DATE) would make much more sense to me.
But to solve your problem (this is T-SQL "UPDATE FROM" syntax, but I think Oracle knows it):
UPDATE
Table1
SET
Date = Table2Aggregate.MinDate
FROM
Table1
INNER JOIN (
SELECT Id, SUM(Amount1) SumAmount1, MIN(Date) MinDate
FROM Table2
GROUP BY Id
) AS Table2Aggregate ON Table1.Id = Table2Aggregate.ID
WHERE
Table1.Amount - Table2Aggregate.SumAmount1 <= 0