Simplifying SELECT statement - sql

so I have a statement I believe should work... However it feels pretty suboptimal and I can't for the life of me figure out how to optimise it.
I have the following tables:
Transactions
[Id] is PRIMARY KEY IDENTITY
[Hash] has a UNIQUE constraint
[BlockNumber] has an Index
Transfers
[Id] is PRIMARY KEY IDENTITY
[TransactionId] is a Foreign Key referencing [Transactions].[Id]
TokenPrices
[Id] is PRIMARY KEY IDENTITY
TokenPriceAttempts
[Id] is PRIMARY KEY IDENTITY
[TransferId] is a Foreign Key referencing [Transfers].[Id]
What I want to do, is select all the transfers, with a few bits of data from their related transaction (one transaction to many transfers), where I don't currently have a price stored in TokenPrices related to that transfer.
In the first part of the query, I am getting a list of all the transfers, and calculating the DIFF between the nearest found token price. If one isn't found, this is null (what I ultimately want to select). I am allowing 3 hours either side of the transaction timestamp - if nothing is found within that timespan, it will be null.
Secondly, I am selecting from this set, ensuring first that diff is null as this means the price is missing, and finally that token price attempts either doesn't have an entry for attempting to get a price, or if it does than it has fewer than 5 attempts listed and the last attempt was more than a week ago.
The way I have laid this out results in essentially 3 of the same / similar SELECT statements within the WHERE clause, which feels hugely suboptimal...
How could I improve this approach?
WITH [transferDateDiff] AS
(
SELECT
[t1].[Id],
[t1].[TransactionId],
[t1].[From],
[t1].[To],
[t1].[Value],
[t1].[Type],
[t1].[ContractAddress],
[t1].[TokenId],
[t2].[Hash],
[t2].[Timestamp],
ABS(DATEDIFF(SECOND, [tp].[Timestamp], [t2].[Timestamp])) AS diff
FROM
[dbo].[Transfers] AS [t1]
LEFT JOIN
[dbo].[Transactions] AS [t2]
ON [t1].[TransactionId] = [t2].[Id]
LEFT JOIN
(
SELECT
*
FROM
[dbo].[TokenPrices]
)
AS [tp]
ON [tp].[ContractAddress] = [t1].[ContractAddress]
AND [tp].[Timestamp] >= DATEADD(HOUR, - 3, [t2].[Timestamp])
AND [tp].[Timestamp] <= DATEADD(HOUR, 3, [t2].[Timestamp])
WHERE
[t1].[Type] < 2
)
SELECT
[tdd].[Id],
[tdd].[TransactionId],
[tdd].[From],
[tdd].[To],
[tdd].[Value],
[tdd].[Type],
[tdd].[ContractAddress],
[tdd].[TokenId],
[tdd].[Hash],
[tdd].[Timestamp]
FROM
[transferDateDiff] AS tdd
WHERE
[tdd].[diff] IS NULL AND
(
(
SELECT
COUNT(*)
FROM
[dbo].[TokenPriceAttempts] tpa
WHERE
[tpa].[TransferId] = [tdd].[Id]
)
= 0 OR
(
(
SELECT
COUNT(*)
FROM
[dbo].[TokenPriceAttempts] tpa
WHERE
[tpa].[TransferId] = [tdd].[Id]
)
< 5 AND
(
DATEDIFF(DAY,
(
SELECT
MAX([tpa].[Created])
FROM
[dbo].[TokenPriceAttempts] tpa
WHERE
[tpa].[TransferId] = [tdd].[Id]
),
CURRENT_TIMESTAMP
) >= 7
)
)
)

Here is an attempt to help simplify. I stripped out all the [brackets] that really are not required unless you are running into something like a reserved keyword, or columns with spaces in their name (bad to begin with).
Anyhow, your main query had 3 instances of a select per ID. To eliminate that, I did a LEFT JOIN to a subquery that pulls all transfers of type < 2 AND JOINS to the price attempts ONCE. This way, the result will have already pre-aggregated the count(*) and Max(Created) done ONCE for the same basis of transfers in question with your WITH CTE declaration. So you dont have to keep running the 3 queries each time, and you dont have to query the entire table of ALL transfers, just those with same underlying type < 2 condition. The result subquery alias "PQ" (preQuery)
This now simplifies the readability of the outer WHERE clause from the redundant counts per Id.
WITH transferDateDiff AS
(
SELECT
t1.Id,
t1.TransactionId,
t1.From,
t1.To,
t1.Value,
t1.Type,
t1.ContractAddress,
t1.TokenId,
t2.Hash,
t2.Timestamp,
ABS( DATEDIFF( SECOND, tp.Timestamp, t2.Timestamp )) AS diff
FROM
dbo.Transfers t1
LEFT JOIN dbo.Transactions t2
ON t1.TransactionId = t2.Id
LEFT JOIN dbo.TokenPrices tp
ON t1.ContractAddress = tp.ContractAddress
AND tp.Timestamp >= DATEADD(HOUR, - 3, t2.Timestamp)
AND tp.Timestamp <= DATEADD(HOUR, 3, t2.Timestamp)
WHERE
t1.Type < 2
)
SELECT
tdd.Id,
tdd.TransactionId,
tdd.From,
tdd.To,
tdd.Value,
tdd.Type,
tdd.ContractAddress,
tdd.TokenId,
tdd.Hash,
tdd.Timestamp
FROM
transferDateDiff tdd
LEFT JOIN
( SELECT
t1.Id,
COUNT(*) Attempts,
MAX(tpa.Created) MaxCreated
FROM
dbo.Transfers t1
JOIN dbo.TokenPriceAttempts tpa
on t1.Id = tpa.TransferId
WHERE
t1.Type < 2
GROUP BY
t1.Id ) PQ
on tdd.Id = PQ.Id
WHERE
tdd.diff IS NULL
AND ( PQ.Attempts IS NULL
OR PQ.Attempts = 0
OR ( PQ.Attempts < 5
AND DATEDIFF(DAY, PQ.MaxCreated, CURRENT_TIMESTAMP ) >= 7
)
)
REVISED to remove the WITH CTE into a single query
SELECT
t1.Id,
t1.TransactionId,
t1.From,
t1.To,
t1.Value,
t1.Type,
t1.ContractAddress,
t1.TokenId,
t2.Hash,
t2.Timestamp
FROM
-- Now, this pre-query is left-joined to token price attempts
-- so ALL Transfers of type < 2 are considered
( SELECT
t1.Id,
coalesce( COUNT(*), 0 ) Attempts,
MAX(tpa.Created) MaxCreated
FROM
dbo.Transfers t1
LEFT JOIN dbo.TokenPriceAttempts tpa
on t1.Id = tpa.TransferId
WHERE
t1.Type < 2
GROUP BY
t1.Id ) PQ
-- Now, we can just directly join to transfers for the rest
JOIN dbo.Transfers t1
on PQ.Id = t1.Id
-- and the rest from the WITH CTE construct
LEFT JOIN dbo.Transactions t2
ON t1.TransactionId = t2.Id
LEFT JOIN dbo.TokenPrices tp
ON t1.ContractAddress = tp.ContractAddress
AND tp.Timestamp >= DATEADD(HOUR, - 3, t2.Timestamp)
AND tp.Timestamp <= DATEADD(HOUR, 3, t2.Timestamp)
WHERE
ABS( DATEDIFF( SECOND, tp.Timestamp, t2.Timestamp )) IS NULL
AND ( PQ.Attempts = 0
OR ( PQ.Attempts < 5
AND DATEDIFF(DAY, PQ.MaxCreated, CURRENT_TIMESTAMP ) >= 7 )
)

I don't understand why you are doing this:
LEFT JOIN
(
SELECT
*
FROM
[dbo].[TokenPrices]
)
AS [tp]
ON [tp].[ContractAddress] = [t1].[ContractAddress]
AND [tp].[Timestamp] >= DATEADD(HOUR, - 3, [t2].[Timestamp])
AND [tp].[Timestamp] <= DATEADD(HOUR, 3, [t2].[Timestamp])
Isn't this a
LEFT JOIN [dbo].[TokenPrices] as TP ...
This:
SELECT
COUNT(*)
FROM
[dbo].[TokenPriceAttempts] tpa
WHERE
[tpa].[TransferId] = [tdd].[Id]
could be another CTE instead of being a sub...
In fact any of your sub queries could be CTE's, that's part of a CTE is making things easier to read.
,TPA
AS
(
SELECT COUNT(*)
FROM [dbo].[TokenPriceAttempts] tpa
WHERE [tpa].[TransferId] = [tdd].[Id]
)

Related

Variables Declaration, CTEs, and While Loops in Oracle SQL

So I might be stuck at something very trivial but can't figure out how to make it work. I create a 2 blocks of code that work in SQL but I have some problems with the date variable declaration in Oracle SQL.
I had write access to the SQL database when I create these codes so I did a 'Insert Into' to create temp tables. I don't have write access anymore. So I am using CTEs for it.
The original code looks like this:
DECLARE #Startdate Datetime = '2021-Jun-01 00:00:00.000'
DECLARE #Enddate Datetime = '2021-Jun-30 00:00:00.000'
Insert into Temp1
select ...
from ...
WHILE Startdate <= Enddate
BEGIN
Insert into Temp2
select ...
from (Temp 1)
left join
select ...
set #startdate=dateadd(d,1,#startdate)
end;
With my new code, I have made the following adjustmnets:
VARIABLE Startdate Datetime = '2021-Jun-01 00:00:00.000'
VARIABLE Enddate Datetime = '2021-Jun-30 00:00:00.000'
EXEC :Startdate := '2021-Jun-30 00:00:00.000'
EXEC :Enddate := '2021-Jun-30 00:00:00.000'
WITH Temp1 as (
select ...
from ...),
/* Unsure about using WHILE with with 2 CTEs so removing them for now but will need to be added*/
WITH Temp2 as
select ...
from (Temp 1)
left join
select ...
set startdate = :startdate + 1
end)
select * from Temp2;
The 2 blocks of code work perfectly individually. I think my concern lies with one or all of the following:
Variable Declaration - I read a couple of stackoverflow posts and it seems like there is binding variable and substitution variable. Is there a different way to declare variables?
The WHILE Loop specially between 2 CTEs. Can we do a while loop as a CTE? (similar to this) create while loop with cte
How the date is incremented. Is this the proper way to increment dates in Oracle PL/SQL?
Any guidance would be helpful.
Also adding 2 blocks of codes for reference:
Details of Tables:
Transactions - Contains Transaction information. Execution Date is a timestamp of the transaction execution
Account - Contains Account Information with a unique Account_Key for every account
Code_Rel - Maps the transaction code to a transaction type
Group Rel - Maps the transaction type to a transaction group
/***Block 1 of Code***/
insert into Temp1
select
a.ACCOUNT_KEY
,a.SPG_CD
,t.EXECUTION_DATE
from Schema_Name.TRANSACTIONS t
inner join Schema_Name.ACCOUNT a on a.en_sk=t.ac_sk
inner join Schema_Name.Code_Rel tr on t.t_cd_s = tr.t_cd_s
inner join ( select * from Schema_Name.Group_Rel
where gtrt_cd in ('Type1','Type2')) tt on tr.trt_cd = tt.trt_cd
where t.EXECUTION_DATE >= #startdate and t.EXECUTION_DATE<=#EndDt
and tt.gtrt_cd in ('Type1','Type2')
group by a.ACCOUNT_KEY ,a.SPG_CD, t.EXECUTION_DATE;
/***WHILE LOOP***/
while #startdate <= #EndDt
BEGIN
/***INSERT AND BLOCK 2 OF CODE***/
insert into Temp2
select table1.account_key, table1.SPG_CD, #startdate, coalesce(table2.sum_tr1,0),coalesce(table3.sum_tr2,0),
case when coalesce(table3.sum_tr2,0)>0 THEN coalesce(table2.sum_tr1,0)/coalesce(table3.sum_tr2,0) ELSE 0 END,
case when coalesce(table3.sum_tr2,0)>0 THEN
CASE WHEN coalesce(table2.sum_tr1,0)/coalesce(table3.sum_tr2,0)>=0.9 and coalesce(table2.sum_tr1,0)/coalesce(table3.sum_tr2,0)<=1.10 and coalesce(table2.sum_tr1,0)>=1000 THEN 'Yes' else 'No' END
ELSE 'No' END
FROM ( SELECT * FROM Temp1 WHERE execution_date=#startdate) TABLE1 LEFT JOIN
(
select a.account_key,a.SPG_CD, SUM(t.AC_Amt) as sum_tr1
from Schema_Name.TRANSACTIONS t
inner join Schema_Name.ACCOUNT a on a.en_sk=t.ac_sk
inner join Schema_Name.Code_Rel tr on t.t_cd_s = tr.t_cd_s
inner join ( select * from Schema_Name.Group_Rel
where gtrt_cd in ('Type1')) tt on tr.trt_cd = tt.trt_cd
where t.EXECUTION_DATE <= #startdate
and t.EXECUTION_DATE >=dateadd(day,-6,#startdate)
and tt.gtrt_cd in ('Type1')
group by a.account_key, a.SPG_CD
) table2 ON table1.account_key=table2.account_key
LEFT JOIN
(
select a.account_key,a.SPG_CD, SUM(t.AC_Amt) as sum_tr2
from Schema_Name.TRANSACTIONS t
inner join Schema_Name.ACCOUNT a on a.en_sk=t.ac_sk
inner join Schema_Name.Code_Rel tr on t.t_cd_s = tr.t_cd_s
inner join ( select * from Schema_Name.Group_Rel
where gtrt_cd in ('Type2')) tt on tr.trt_cd = tt.trt_cd
where t.EXECUTION_DATE <= #startdate
and t.EXECUTION_DATE >=dateadd(day,-6,#startdate)
and tt.gtrt_cd in ('Type2')
group by a.account_key, a.SPG_CD ) table3 on table1.account_key=table3.account_key
where coalesce(table2.sum_tr1,0)>=1000
set #startdate=dateadd(d,1,#startdate)
end;
You do not need to use PL/SQL or a WHILE loop or to declare variables and can probably do it all in a single SQL query using subquery factoring clauses (and recursion) to generate a calendar of incrementing dates. Something like this made-up example:
INSERT INTO temp2 (col1, col2, col3)
WITH time_bounds(start_date, end_date) AS (
-- You can declare the bounds in the query.
SELECT DATE '2021-06-01',
DATE '2021-06-30'
FROM DUAL
),
calendar (dt, end_date) AS (
-- Recursive query to generate a row for each day.
SELECT start_date, end_date FROM time_bounds
UNION ALL
SELECT dt + INTERVAL '1' DAY, end_date
FROM calendar
WHERE dt + INTERVAL '1' DAY <= end_date
),
temp1 (a, b, c) AS (
-- Made-up query
SELECT a, b, c FROM some_table
),
temp2 (a, d, e) AS (
-- Another made-up query.
SELECT t1.a,
s2.d,
s2.e
FROM temp1 t1
LEFT OUTER JOIN some_other_table s2
ON (t1.b = s2.b)
)
-- Get the values to insert.
SELECT t2.a,
t2.d,
t2.e
FROM temp2 t2
INNER JOIN calendar c
ON (t2.e = c.dt)
WHERE a BETWEEN 3.14159 AND 42;
If you try doing it with multiple inserts in a PL/SQL loop then it will be much slower than a single statement.

Email On Results Of Query On Condition

I have a query that gets the 2 newest datetime values. I need to get a diff of the datetime and email if the value is greater than 5 minutes. I have accomplished it using 3 tables but i would like to do it in a single query.
SELECT DATEDIFF(minute, T1, T2) + 10 As DIFF
FROM
(SELECT TOP 1 T1.DateAndTime As T1, T2.DateAndTime AS T2
FROM [WIMS_SQL].[dbo].[FloatTable] as T1
Inner Join [WIMS_SQL].[dbo].[FloatTable] as T2
On T1.DateAndTime > T2.DateAndTime
Order By T2.DateAndTime Desc)
IF DIFF > 5 "EMAIL FUNCTION"
Im sure theres a better way to do this than i am using
Here's the logic with a CTE.
declare #minutes int
;with cte as(
select top 2 DateAndTime
from [WIMS_SQL].[dbo].[FloatTable]
order by DateAndTime desc)
select
#minutes = datediff(minute, max(DateAndTime), min(DateAndTime)) + 10
from cte
if (#minutes > 5)
begin
exec msdb..sp_send_dbmail
#profile_name = 'Profile Name You Set Up'
,#recipients = 'someone#domain.com'
,#subject = 'some subject'
end
ONLINE DEMO
This method will prevent you from making a cartesian product with that join condition and should be a lot faster.

difficult sql query

I have a table containing many columns, I have to make my selection according to these two columns:
TIME ID
-216 AZA
215 AZA
56 EA
-55 EA
66 EA
-03 AR
03 OUI
-999 OP
999 OP
04 AR
87 AR
The expected output is
TIME ID
66 EA
03 OUI
87 AR
I need to select the rows with no matches. There are rows which have the same ID, and almost the same time but inversed with a little difference. For example the first row with the TIME -216 matches the second record with time 215. I tried to solve it in many ways, but everytime I find myself lost.
First step -- find rows with duplicate IDs. Second step -- filter for rows which are near-inverse duplicates.
First step:
SELECT t1.TIME, t2.TIME, t1.ID FROM mytable t1 JOIN mytable
t2 ON t1.ID = t2.ID AND t1.TIME > t2.TIME;
The second part of the join clause ensures we only get one record for each pair.
Second step:
SELECT t1.TIME,t2.TIME,t1.ID FROM mytable t1 JOIN mytable t2 ON t1.ID = t2.ID AND
t1.TIME > t2.TIME WHERE ABS(t1.TIME + t2.TIME) < 3;
This will produce some duplicate results if eg. (10, FI), (-10, FI) and (11, FI) are in your table as there are two valid pairs. You can possibly filter these out as follows:
SELECT t1.TIME,MAX(t2.TIME),t1.ID FROM mytable t1 JOIN mytable t2 ON
t1.ID = t2.ID AND t1.TIME > t2.TIME WHERE ABS(t1.TIME + t2.TIME) < 3 GROUP BY
t1.TIME,t1.ID;
But it's unclear which result you want to drop. Hopefully this points you in the right direction, though!
Does this help?
create table #RawData
(
[Time] int,
ID varchar(3)
)
insert into #rawdata ([time],ID)
select -216, 'AZA'
union
select 215, 'AZA'
union
select 56, 'EA'
union
select -55, 'EA'
union
select 66, 'EA'
union
select -03, 'AR'
union
select 03, 'OUI'
union
select -999, 'OP'
union
select 999, 'OP'
union
select 04, 'AR'
union
select 87, 'AR'
union
-- this value added to illustrate that the algorithm does not ignore this value
select 156, 'EA'
--create a copy with an ID to help out
create table #Data
(
uniqueId uniqueidentifier,
[Time] int,
ID varchar(3)
)
insert into #Data(uniqueId,[Time],ID) select newid(),[Time],ID from #RawData
declare #allowedDifference int
select #allowedDifference = 1
--find duplicates with matching inverse time
select *, d1.Time + d2.Time as pairDifference from #Data d1 inner join #Data d2 on d1.ID = d2.ID and (d1.[Time] + d2.[Time] <=#allowedDifference and d1.[Time] + d2.[Time] >= (-1 * #allowedDifference))
-- now find all ID's ignoring these pairs
select [Time],ID from #data
where uniqueID not in (select d1.uniqueID from #Data d1 inner join #Data d2 on d1.ID = d2.ID and (d1.[Time] + d2.[Time] <=3 and d1.[Time] + d2.[Time] >= -3))

SQL query help - top N values of T.a grouped by T.b, T.c

I have a table called TaskLog that holds the results of various scheduled tasks. It has (for the purposes of this question) these columns:
TaskLogID: unique ID for this record
TaskID: ID of the task that ran
HostName: name of the host on which it ran
RunDate: date and time on which the task was run
Output: output of this run
In order to get the output from the latest run of each task, I had been executing multiple queries, until I worked out this single query which is much faster:
SELECT TaskLog.TaskID, TaskLog.HostName, TaskLog.Output
FROM TaskLog
INNER JOIN (
SELECT TaskLogID, TaskID, HostName, MAX(RunDate)
FROM TaskLog
GROUP BY TaskID, HostName
) AS Latest
USING (TaskLogID)
Now I'd like to get the output from each of the last N runs of each task, for some fixed N, instead of just the latest run. Is there a way to do this in a single query?
TIA
Untested as I don't have MySQL installed on this machine (based on here)
select TaskLogID,
TaskID,
HostName,
RunDate
from (select TaskLogID,
TaskID,
HostName,
RunDate,
#num := if(#group = concat(TaskID, HostName), #num + 1, 1) as row_number,
#group := concat(TaskID, HostName) as dummy
from TaskLog) as x
where row_number <= 5;
This is where MySQL lack of window functions such as Row_Number() really hurts.
Select T.TaskLogId, T.TaskId, T.HostName, T.RunDate
From TaskLog As T
Join (
Select T1.TaskLogId
, (Select Count(*)
From TaskLog as T2
Where T2.TaskId = T1.TaskId
And T2.RunDate < T1.RunDate) + 1 As Rnk
From TaskLog As T1
) As RankedTasks
On RankedTasks.TaskLogId = T.TaskLogId
And RankedTasks.Rnk <= <somevalue>
Order By T.TaskId, T.RunDate
ADDITION
Assuming that TaskLogId is an auto increment column, you might be able to something like the following (In this example, I assumed you requested the top 5 items):
Select T.TaskLogId, T.TaskId, T.HostName, T.RunDate
From TaskLog As T
Join (
Select Tasks1.TaskId
, (
Select T4.TaskLogId
From TaskLog As T4
Where T4.TaskId = Tasks.TaskId
Order By T4.RunDate Desc
Limit 5, 1
) As UpperTaskLogId
From (
Select T3.TaskId
From TaskLog As T3
Group By T3.TaskId
) As Tasks1
) As LastId
On LastId.TaskId = T.TaskId
And LastId.UpperTaskLogId >= T.TaskLogId

Erroneous ORA-01427: single-row subquery returns more than one row

I'm getting the error [ORA-01427: single-row subquery returns more than one row] when I execute a query. I have a query structured like so:
SELECT LV.PRICE,
(SELECT C.MODEL_NAME FROM CARS C WHERE C.MODEL_ID = LV.MODEL_ID) as MODEL_NAME
FROM LEDGER_VIEW LV
WHERE LV.PRICE < 500
It's breaking on the nested select. I know the logic both in the view and in this query is correct, and that there's no chance of the nested select returning more than one row. The CARS table's MODEL_ID is a unique field. If I execute the query without the nested select it doesn't return this error.
The LEDGER_VIEW is a view built on top of another view. Is it possible that these stacked views are buggy in Oracle 10g? I don't know how else to debug this problem.
I am aware I could change this particular query to a join rather than a nested select, but I'd like to know why this is happening because I use nested queries in other places where it is not so easily modifiable.
EDIT: Here's the really strange thing. The LEDGER_VIEW is, as I said, built on top of another view. As a test, I copied the nested view's SQL directly into the SQL of the SQL of LEDGER_VIEW, in place of the nested view, and it returned with no errors (as expected). This seems to confirm to me that there is some buggy behavior either with nested views or with the combination of nested views + database links.
Your subquery is returning multiple rows. Use the query below to find out which MODELID values in the Car table are duplicated:
select MODELID as CarsModelID, count(*) as Count
from cars
where MODELID in (
select MODEL_ID
from LEDGER_VIEW
WHERE LV.PRICE < 500
)
group by MODELID
having count(*) > 1
I am unable to recreate via a creation of a stacked view. (althoug RedFilters will find the culprit)
CREATE TABLE t1
(
t1_id NUMBER ,
txt VARCHAR2( 50 ),
CONSTRAINT t1_pk PRIMARY KEY( t1_id )
) ;
CREATE TABLE t2
(
t2_id NUMBER ,
t1_id NUMBER ,
price NUMBER( 10, 4 ) ,
CONSTRAINT t2_pk PRIMARY KEY( t2_id ),
CONSTRAINT t2_fk FOREIGN KEY( t1_id ) REFERENCES t1( t1_id )
);
insert into t1(t1_id, txt) values(1,'fit');
insert into t1(t1_id, txt) values(2,'focus');
insert into t1(t1_id, txt) values(3,'golf');
insert into t1(t1_id, txt) values(4,'explorer');
insert into t1(t1_id, txt) values(5,'corolla');
insert into t2(t2_id, t1_id, price) values(1,1,17000);
insert into t2(t2_id, t1_id, price) values(2,2,16000);
insert into t2(t2_id, t1_id, price) values(3,3,22000);
insert into t2(t2_id, t1_id, price) values(4,4,31000);
insert into t2(t2_id, t1_id, price) values(5,5,17000);
create view t1_view as select * from t1;
create view t2_view as select * from t2;
create view t_stacked_view as
select t1_view.txt ,
t2_view.price ,
t1_view.t1_id
from t1_view
left join
t2_view
on t1_view.t1_id = t2_view .t1_id
;
--stacked view test
select t1_view.txt ,
(select t_stacked_view.price
from t_stacked_view
where t1_view.t1_id = t_stacked_view .t1_id) price
from t1_view ;
--or better yet, just drop the row level query
select t1_view.txt ,
t2_view.price
from t1_view
left join
t2_view
on t1_view.t1_id = t2_view .t1_id
;
But that begs the question, why are you doing the row level query here? While 10g ought to optimize them the same, I have always found it easier to write queries as below, both for readability, maintainability, and to specifically avoid the error you are having (is it always, 3 years down the road, guaranteed by the application (both in the db and the calling app) that you cannot have a condition that will cause this error? One rouge statement gets in and your entire app dies?
SELECT LV.PRICE,
c.model_name
FROM LEDGER_VIEW LV
LEFT /* OR INNER */ JOIN CARS C
ON C.MODEL_ID = LV.MODEL_ID
WHERE LV.PRICE < 500
I suggest using RedFilter's answer to check whether there are multiple cars with a given MODEL_ID.
If you're absolutely certain that CARS.MODEL_ID is unique, then it implies that the error message is generated by selection from LEDGER_VIEW - so try running the equivalent query without the subquery on CARS, like so:
SELECT LV.PRICE
FROM LEDGER_VIEW LV
WHERE LV.PRICE < 500
If you still see the same error (you should, if CARS.MODEL_ID is unique) you will need to debug LEDGER_VIEW - ie. check for sub-queries returning multiple rows in LEDGER_VIEW and the underlying views it is based on.
Creating views based on views is possible in most forms of SQL, but it is usually a bad idea - for this very reason.
Try forcing your subquery to return a single result by appending rownum = 1, like this:
SELECT LV.PRICE,
(SELECT C.MODEL_NAME FROM CARS C WHERE C.MODEL_ID = LV.MODEL_ID AND ROWNUM = 1) as MODEL_NAME
FROM LEDGER_VIEW LV
WHERE LV.PRICE < 500
It will probably work and if it does, you will know that your subquery returns multiple rows, which judging by the error code it should be. Of course this is not a solution so you might have to fix your data in cars table to actually solve the problem. Leaving and rownum = 1 will eliminate the error if model_id is duplicated again, preventing you from noticing the problem.
select
a.account_number,
a.party_id,
a.TRX_NUMBER,
a.trx_date,
a.order_number,
adv.unapplied_amt,
a.Finance,
a.customer_name,a.PARTY_NAME,
a.customer_number,a.contact_number,
a.name,
a.Aging,
a.transaction_type,
a.exec_name,
a.team_leader,
sum(a.O_SAmount),
(case when (trunc(sysdate) - trunc(a.trx_date)) <=:ag1 then sum(a.O_SAmount) else 0 end ) bucket1,--"<" || :ag1,
(case when (trunc(sysdate) - trunc(a.trx_date)) between :ag1+1 and :ag2 then sum(a.O_SAmount) else 0 end ) bucket2,--:ag1+1 || "to" || :ag2,
(case when (trunc(sysdate) - trunc(a.trx_date)) between :ag2+1 and :ag3 then sum(a.O_SAmount) else 0 end ) bucket3,--:ag2+1 || "to" || :ag3,
(case when (trunc(sysdate) - trunc(a.trx_date)) >:ag3 then sum(a.O_SAmount) else 0 end ) bucket4,
:AS_ON_date
from
(select distinct hca.account_number,hp.party_id,--rcta.CUSTOMER_TRX_ID,
--rcta.trx_number,rcta.trx_date,apsa.due_date,
(
select distinct
--ooha.order_number,
rcta.trx_number
--to_char(rcta.trx_date,'DD-MON-YYYY') trx_date
from
ra_customer_trx_all rcta,
oe_order_headers_all ooh,
oe_order_lines_all oola,
--ra_customer_trx_all rcta,
ra_customer_trx_lines_all rctla,
ra_cust_trx_types_all rctta
--ra_customer_trx_lines_all rctl
where 1=1
AND ooh.header_id = oola.header_id
--AND ooh.order_number = '111111010101698'
AND ooh.order_number=oohA.order_number
AND TO_CHAR (ooh.order_number) = rcta.ct_reference
AND rcta.customer_trx_id = rctla.customer_trx_id
AND rctla.inventory_item_id = oola.inventory_item_id
and rcta.CUST_TRX_TYPE_ID = rctta.cust_trx_type_id
and rcta.org_id = rctta.org_id
and rctta.type like 'INV'
and oola.ordered_item LIKE 'MV%'
AND oola.attribute3 = 'Y'
AND ooh.flow_status_code <> 'ENTERED'
AND oola.flow_status_code <> 'CANCELLED'
)TRX_NUMBER,
(select distinct
--ooha.order_number,
--rcta.trx_number
rcta.trx_date
from
ra_customer_trx_all rcta,
oe_order_headers_all ooh,
oe_order_lines_all oola,
--ra_customer_trx_all rcta,
ra_customer_trx_lines_all rctla,
ra_cust_trx_types_all rctta
--ra_customer_trx_lines_all rctl
where 1=1
AND ooh.header_id = oola.header_id
--AND ooh.order_number = '111111010101698'
AND ooh.order_number=oohA.order_number
AND TO_CHAR (ooh.order_number) = rcta.ct_reference
AND rcta.customer_trx_id = rctla.customer_trx_id
AND rctla.inventory_item_id = oola.inventory_item_id
and rcta.CUST_TRX_TYPE_ID = rctta.cust_trx_type_id
and rcta.org_id = rctta.org_id
and rctta.type like 'INV'
and oola.ordered_item LIKE 'MV%'
AND oola.attribute3 = 'Y'
AND ooh.flow_status_code <> 'ENTERED'
AND oola.flow_status_code <> 'CANCELLED'
)TRX_Date,
rcta.INTERFACE_HEADER_ATTRIBUTE1 order_number,
ooha.attribute10 Finance,
f.customer_name,HP.PARTY_NAME,
TO_NUMBER(f.customer_number)customer_number,hp.primary_phone_number contact_number,--csi.incident_number,
--cii.instance_number,
haou.name,
--sum(acr.amount) Advance,--rcta.CUST_TRX_TYPE_ID,--acr.cash_receipt_id,
--sum(abs((apsa.AMOUNT_DUE_REMAINING-nvl(acr.amount,0)))) "O_SAmount",
apsa.AMOUNT_DUE_REMAINING O_SAmount,
--sum(abs((apsa.AMOUNT_DUE_REMAINING))) "O_SAmount",
round(months_between(sysdate,rcta.trx_date)*30) Aging,
--(case when ((round(months_between(sysdate,rcta.trx_date)*30)>=0) or (round(months_between(sysdate,rcta.trx_date)*30)<:aging1)) then apsa.AMOUNT_DUE_REMAINING end) "0 TO 30"
--(case when (trunc(sysdate) - trunc(apsa.Due_Date)) <=:ag1 then apsa.AMOUNT_DUE_REMAINING else 0 end ) bucket1,--"<" || :ag1,
--(case when (trunc(sysdate) - trunc(apsa.Due_Date)) between :ag1+1 and :ag2 then apsa.AMOUNT_DUE_REMAINING else 0 end ) bucket2,--:ag1+1 || "to" || :ag2,
--(case when (trunc(sysdate) - trunc(apsa.Due_Date)) between :ag2+1 and :ag3 then apsa.AMOUNT_DUE_REMAINING else 0 end ) bucket3,--:ag2+1 || "to" || :ag3,
--(case when (trunc(sysdate) - trunc(apsa.Due_Date)) >:ag3 then apsa.AMOUNT_DUE_REMAINING else 0 end ) bucket4,
--apsa.amount_due_original,
--TO_NUMBER(apsa.AMOUNT_DUE_REMAINING)AMOUNT_DUE_REMAINING,
rctta.name transaction_type,
PAPF.full_name||'-'||PAPF.EMPLOYEE_NUMBER exec_name,
ooha.attribute9 team_leader,
:AS_ON_date
from ra_customer_trx_all rcta,
oe_order_headers_all ooha,
hz_cust_accounts hca,
hz_parties hp,
--cs_incidents_all_b csi,
--csi_item_instances cii,
hr_all_organization_units haou,
ar_cash_receipts_all acr,
ar_receivable_applications_all aaa,
ra_cust_trx_types_all RCTTA,
hr.per_all_people_f papf,
ar_customers f,
ar_payment_schedules_all apsa,
jtf.JTF_RS_SALESREPS jrs
where 1=1
--and INTERFACE_HEADER_ATTRIBUTE1 like '111111060100538'
--and INTERFACE_HEADER_ATTRIBUTE1 like '111111010105402'
--and INTERFACE_HEADER_ATTRIBUTE1 like '111111010102791'
and rcta.ct_reference(+)=TO_CHAR(ooha.order_number)
AND f.customer_id = (rcta.bill_to_customer_id)
and f.customer_id=hca.cust_account_id
and hca.party_id=hp.party_id
and haou.organization_id=rcta.INTERFACE_HEADER_ATTRIBUTE10
--and hp.party_id=cii.owner_party_id
--and csi.inventory_item_id=cii.inventory_item_id
--and csi.inv_organization_id=haou.organization_id
--and haou.organization_id=nvl(:location,haou.organization_id)
and ooha.SHIP_FROM_ORG_ID=nvl(:location,haou.organization_id)
AND RCTTA.NAME like :transaction_type||'%'
--decode(:org_id,null,null,(select name from ar_cash_receipts_all where organization_id = :org_id)) ||'%')
and rcta.trx_date<=to_date(:AS_ON_date)
--AND RCTTA.NAME=NVL(:TRANS_TYPE,RCTTA.NAME)
and rcta.org_id=nvl(:org_id,rcta.org_id)
--and f.customer_name like 'VIKAS SATAV'
and aaa.applied_customer_trx_id(+)=rcta.customer_trx_id
and aaa.cash_receipt_id=acr.cash_receipt_id(+)
and rcta.status_trx like 'OP'
and rcta.CUST_TRX_TYPE_ID=rctta.CUST_TRX_TYPE_ID
and apsa.CUSTOMER_TRX_ID=rcta.CUSTOMER_TRX_ID
and TO_NUMBER(apsa.AMOUNT_DUE_REMAINING) >0
--and hp.party_id=papf.party_id(+)
and jrs.salesrep_id = ooha.SALESREP_ID
and jrs.ORG_ID = ooha.ORG_ID
and jrs.PERSON_ID = papf.PERSON_ID(+)
) a,
(
select
b.order_number,
sum(b.AMOUNT_APPLIED) unapplied_amt
from
(select distinct to_char(ooha.order_number) order_number,ara.* from
oe_order_headers_all ooha,
oe_payments oe,
ar_receivable_applications_all ara
where 1=1--ooha.order_number = :p_order_num
and oe.header_id=ooha.header_id
and ara.PAYMENT_SET_ID=oe.PAYMENT_SET_ID
and ara.DISPLAY='Y'
and (ara.STATUS like 'OTHER ACC' or ara.STATUS like 'UNAPP') --or ara.STATUS like 'ACC')
) b
group by b.order_number
) adv
where adv.order_number(+)=a.order_number
group by a.account_number,
a.party_id,
a.TRX_NUMBER,
a.trx_date,
a.order_number,
adv.unapplied_amt,
a.Finance,
a.customer_name,a.PARTY_NAME,
a.customer_number,a.contact_number,
a.name,
a.Aging,
a.transaction_type,
a.exec_name,
a.team_leader
order by a.Aging desc