Not Exists query - sql

I'm trying to find the clients, those who didn't order in the last 2 years and they ordered this year more than 500.. I wrote this query and I used the "NOT EXISTS" condition, but it is still showing me the wrong results.
Some suggestions would be appreciated.
My code:
SELECT
"Sales"."Kundennummer" as 'Neuer Kunde',
year("Sales"."Datum"),
sum("Sales"."Umsatz mit Steuer") as "Umsatz"
FROM "Sales"
WHERE year("Sales"."Datum") = '2017'
AND NOT EXISTS
(
SELECT "Sales"."Kundennummer"
FROM "Sales"
WHERE year("Sales"."Datum") = '2015'
AND year("Sales"."Datum") = '2016'
)
GROUP BY
"Sales"."Kundennummer",
"Sales"."Datum"
HAVING sum("Sales"."Umsatz mit Steuer") > 500

The query in the NOT EXISTS clause will probably yield 0 rows, since a row can't have Datum both 2015 and 2016. So it should probably be OR instead of AND.
Also, if you fix this, there is no link between the subquery and the superquery, which means that it will return rows for any customer (given that there exists a row with Datum either 2015 or 2016 in your table which I guess it does).
So, something like:
SELECT
"Sales"."Kundennummer" as 'Neuer Kunde',
year("Sales"."Datum"),
sum("Sales"."Umsatz mit Steuer") as "Umsatz"
FROM "Sales" sales
WHERE year("Sales"."Datum") = '2017'
AND NOT EXISTS
(
SELECT "Sales"."Kundennummer"
FROM "Sales" salesI
WHERE salesI."Kundennummer" = sales."Kundennummer"
AND (year("Sales"."Datum") = '2015'
OR year("Sales"."Datum") = '2016')
)
GROUP BY
"Sales"."Kundennummer",
"Sales"."Datum"
HAVING sum("Sales"."Umsatz mit Steuer") > 500

Your EXISTS query is not correlated to the main query, i.e. it doesn't look for data for the Kundennummer in question, but whether there are any records in 2015 and 2016.
(You also have the condition for the years wrong by using AND where it must be OR and you should not use quotes on numbers like 2015', and you should not use single quotes on names like 'Neuer Kunde'.)
It should be
AND NOT EXISTS
(
SELECT *
FROM Sales other_years
WHERE other_years.Kundennummer = Sales.Kundennummer
AND year(other_years.Datum) in (2015, 2016)
)
or uncorrelated with NOT IN
AND Kundennummer NOT IN
(
SELECT Kundennummer
FROM Sales
WHERE year(Datum) in (2015, 2016)
)
Be aware though, that when using NOT IN the subquery must return no nulls. E.g. where 3 not in (1, 2, null) does not result in true, as one might expect, because the DBMS argues that the unknown value (null) might very well be a 3 :-)

I propose you here below 3 different ways to do it:
Joining 2 tables
select this_year_sales.kundenummer, this_year_sales.tot_umsatz
from (select sum(umsatz) tot_umsatz, kundenummer from sales where extract(year from (datum)) = extract(year from sysdate) group by kundenummer) this_year_sales
, (select kundenummer, max(datum) max_datum from sales where datum < trunc(sysdate, 'year') group by kundenummer) previous_sales
where this_year_sales.kundenummer = previous_sales.kundenummer
and extract(year from previous_sales.max_datum) < (extract(year from sysdate)-2)
and this_year_sales.tot_umsatz > 500;
Using NOT INT
select kundenummer, sum(umsatz)
from sales s
where extract(year from datum) = extract(year from sysdate)
and kundenummer not in (select kundenummer from sales where extract(year from datum) > (extract(year from sysdate) - 2) and extract(year from datum) < (extract(year from sysdate)-1))
group by kundenummer
having sum(umsatz) > 500;
Using NOT EXISTS
select kundenummer, sum(umsatz)
from sales s
where extract(year from datum) = extract(year from sysdate)
and not exists(
select s1.kundenummer, s1.datum from sales s1 where extract (year from s1.datum) >= (extract(year from sysdate)-2) and extract(year from s1.datum) < extract (year from sysdate) and s1.kundenummer = s.kundenummer
)
group by kundenummer
having sum(umsatz) > 500;

Related

Count of consecutive days ORACLE SQL

I need help with a query where a need to count de consecutive days like this
select
a.numcad, a.datapu , f.datapu , nvl(to_char(f.datapu, 'DD'),0)dia,
row_number() over (partition by a.numcad, f.datapu order by f.datapu)particao
from
ronda.r066apu a
left join (select t.numcad, t.numemp, t.datacc, t.datapu
from ronda.r070acc t
where t.datacc >= '21/01/2022'
and t.datacc <= trunc(sysdate)
group by t.numcad, t.numemp, t.datacc, t.datapu)f
on a.numemp = f.numemp
and a.numcad = f.numcad
and a.datapu = f.datapu
where a.numcad = 2675
and A.DATAPU >= '21/01/2022'
and A.DATAPU <= trunc(sysdate)
group by a.numcad, a.datapu, f.datapu, f.datacc
order by a.datapu
result is
between 24/01/2022 and 04/02/2022
is 12 days i need know this count , but i will ways get the '21/mes/year'
You can try:
SELECT TO_DATE('2022-01-24', 'YYYY-MM-DD') -
TO_DATE('2022-02-04', 'YYYY-MM-DD')
FROM dual
This returns 21, for example...

Oracle SQL variable/parameter usage in query

I have this query below and I need to change WHERE conditions depending on QUARTER.
Meaning that I have to copy that query and change the DATE conditions to 06.2020 then use UNION.
I have no idea how can I optimize this query only with SQL because I am not able to DEFINE some variables/parameters in SQL (not using PL/SQL).
SELECT sum(a.limit_amount) as LIMIT,
sum(b.balance_amount) as OUTSTANDING,
'LOAN' as TYPE,
'Q1 2020' as QUARTER
FROM accounts a
left join account_balances b
ON a.account_key = b.account_key
AND b.balance_type_key = 16
AND b.balance_date = last_day(to_date('03.2020', 'MM.YYYY'))
WHERE a.account_close_date > last_day(to_date('03.2020', 'MM.YYYY')) and a.account_open_date <= last_day(to_date('03.2020', 'MM.YYYY'))
Thanks for help
If SQL*Developer, or SQLPlus, you can use the 'substitution variables'.
SELECT sum(a.limit_amount) as LIMIT,
sum(b.balance_amount) as OUTSTANDING,
'LOAN' as TYPE,
'Q1 2020' as QUARTER -- << probably want TO_CHAR('&&1', 'Q YYYY') or similar here
FROM accounts a
left join account_balances b
ON a.account_key = b.account_key
AND b.balance_type_key = 16
AND b.balance_date = last_day(to_date('&&1', 'MM.YYYY'))
WHERE a.account_close_date > last_day(to_date('&&1', 'MM.YYYY')) and a.account_open_date <= last_day(to_date('&&1', 'MM.YYYY'))
When you run this it will prompt you for the value.
If you used just '&1' it would prompt you for each occurence.
'&&1' reuses the value of the first occurence.
You could specify different variables using &1, &2, ...
Also you may used named variable as follows:
To prompt for the date :
ACCEPT dt CHAR PROMPT 'ENter the date (MM.YYYY): '
Or setting the value at declaration:
DEFINE dt='03.2020'
Then:
SELECT sum(a.limit_amount) as LIMIT,
sum(b.balance_amount) as OUTSTANDING,
'LOAN' as TYPE,
'Q1 2020' as QUARTER -- << probably want TO_CHAR('&&dt', 'Q YYYY') or similar here
FROM accounts a
left join account_balances b
ON a.account_key = b.account_key
AND b.balance_type_key = 16
AND b.balance_date = last_day(to_date('&&dt', 'MM.YYYY'))
WHERE a.account_close_date > last_day(to_date('&&dt', 'MM.YYYY')) and a.account_open_date <= last_day(to_date('&&dt', 'MM.YYYY'))
Ok first of all a disclaimer:
As I know nothing about your original query's logic - the query I propose only shows the main logic - and probably will not run as is.
Having said that - the approach would be:
Based on parameters :start_y (start year) and :end_y (end year)
generate a list with all the quarters for those years years_and_quarters.
Join the years_and_quarters with the required table based on quarter start and end dates - and then calculate the sums grouping by year and quarter.
with quarters as
(
select 'Q1' quarter, '03' q_end_date, '01' q_start_date from dual
union
select 'Q2' quarter, '06' q_end_date,'04' q_start_date from dual
union
select 'Q3' quarter, '09' q_end_date,'07' q_start_date from dual
union
select 'Q4' quarter, '12' q_end_date,'10' q_start_date from dual
),
years as
(select :start_y + (level-1) as mydate
from dual
connect by level <= (:end_y - :start_y) +1
),
years_and_quarters as (
select * from years,quarters
order by mydate
),
accounts as (
SELECT a.limit_amount ,
b.balance_amount,
'LOAN' as TYPE, b.balance_date,a.account_open_date,a.account_close_date
FROM accounts a
left join account_balances b
ON a.account_key = b.account_key
AND b.balance_type_key = 16
)
select sum(accounts.limit_amount) as LIMIT,
sum(accounts.balance_amount) as OUTSTANDING,
years_and_quarters.quarter,
accounts.TYPE
from
years_and_quarters,accounts
where
trunc(balance_date) = last_day(to_date(q_end_date||'.'||mydate, 'MM.YYYY'))
and trunc(account_close_date) > last_day(to_date(q_end_date||'.'||mydate, 'MM.YYYY')) and trunc(account_open_date) <= last_day(to_date(q_end_date||'.'||mydate, 'MM.YYYY'))
group by years_and_quarters.mydate,years_and_quarters.quarter
How about just aggregate by the quarter?
SELECT sum(a.limit_amount) as LIMIT,
sum(b.balance_amount) as OUTSTANDING,
'LOAN' as TYPE,
to_char(balance_date, '"Q"Q YYYY') as quarter
FROM accounts a LEFT JOIN
account_balances b
ON a.account_key = b.account_key AND
b.balance_type_key = 16 AND
b.balance_date = LAST_DAY(TRUNC(balance_date, 'Q') + INTERVAL '2' MONTH)
WHERE a.account_close_date > LAST_DAY(TRUNC(balance_date, 'Q') + INTERVAL '2' MONTH) AND
a.account_open_date <= LAST_DAY(TRUNC(balance_date, 'Q') + INTERVAL '2' MONTH)
GROUP BY TRUNC(balance_date, 'Q') ;
Your query seems focused on the last day of the quarter. This arrives at that by adding two months to the start and using LAST_DAY().

Trying to join two tables via subquery

Current query below
select
x.OVERALL_ID,
extract(year from y.tsdate) as year,
extract(month from y.tsdate) as month,
round(sum(y.grossamount),2) as "labour cost",
sum(y.reg)+sum(y.ot) as "labour hours"
from
(
select OVERALL_ID,
BOAT_NAME
from TV_VESSEL_VISIT
group by OVERALL_ID,VESSEL_NAME
order by OVERALL_ID, VESSEL_NAME
) as x
inner join
(
select *
from TRANS
where ratedesc = 'Labour'
and opsdesc = 'Ops'
and ACTDESC = 'Nature of Job'
and terminal = 'UKN'
and reconciled = 'Y'
and TSDATE between '20-JAN-01' and '20-JAN-31'
) as y on x.BOAT_NAME = y.BOATNAME
group by OVERALL_ID, extract(year from tsdate), extract(month from tsdate)
order by OVERALL_ID, extract(year from tsdate), extract(month from tsdate);
Desired result is OVERALL_ID's labour cost/hours grouped into year and month
Currently getting the below error
ORA-00933: SQL command not properly ended
00933. 00000 - "SQL command not properly ended"
*Cause:
*Action:
Error at Line: 14 Column: 3
Trying to follow this link https://www.geeksengine.com/database/subquery/subquery-in-join-operation.php
Orable allows AS when declaring a column alias, but does not allow it for a table alias. The subquery alias is the same case as table alias. try ) x instead of ) as x, and the same thing for y. This is one of the syntax errors that causes ORA-00933.
If using subqueries DO NOT use ORDER BY inside those subqueries. There is no good purpose to the ordering, it just isn't required.
Try this:
SELECT
x.OVERALL_ID
, extract(year FROM y.tsdate) AS year
, extract(month FROM y.tsdate) AS month
, round(sum(y.grossamount), 2) AS "labour cost"
, sum(y.reg) + sum(y.ot) AS "labour hours"
FROM (
SELECT DISTINCT
OVERALL_ID
, BOAT_NAME
FROM TV_VESSEL_VISIT
) x
INNER JOIN (
SELECT *
FROM TRANS
WHERE ratedesc = 'Labour'
AND opsdesc = 'Ops'
AND ACTDESC = 'Nature of Job'
AND terminal = 'UKN'
AND reconciled = 'Y'
AND TSDATE >= to_date('2020-01-01','yyy-mm-dd') AND TSDATE < to_date('2020-02-01','yyyy-mm-dd')
) y ON x.BOAT_NAME = y.BOATNAME
GROUP BY
OVERALL_ID
, extract(year FROM tsdate)
, extract(month FROM tsdate)
ORDER BY
OVERALL_ID
, extract(year FROM tsdate)
, extract(month FROM tsdate)
;
Notes: I have removed the unwanted ORDER BY in the upper subquery, and changed this to use SELECT DISTINCT (it will produce the same result as the previous group by subquery). Also I have changed the syntax used for the date range. Please avoid using 2 digit year references, always use the full year. Additionally I always recommend avoiding the use of between for date ranges it is far more predictable to use the combination of >= with < as you see above. Plus I have used to_date() so it is clear what dates I am using for the date range - which will give you every row of data relevant to January 2020.
The previous between syntax could possibly miss a whole day's data

SQL Editing Year

The query works but only give years 1985 values. How do I add unlimited amount of years (1985-2014)
use baseball;
SELECT CAST(tf.franchname AS CHAR(20)), s.yearID, s.lgid, AVG(s.salary)
FROM salaries s, teams t, teamsfranchises tf
WHERE s.teamID = t.teamID AND
t.franchID = tf.franchID AND
s.yearID = 1985 AND
(s.lgid='AL' OR s.lgid='NL') GROUP BY tf.franchname, s.yearID, s.lgid order BY
s.yearID;
You could just use BETWEEN.
Your where clause should then look like
(s.yearID BETWEEN 1985 AND 2014) and
Alternatively you could use the < and > operators:
(s.yearID >= 1984 and <= 2014)
If, for any reason you don't have a continous range of years (You only want 5 years). IN could also be an option:
s.yearID IN (1984, 1991, 1996, 2001, 2006)
Your query has a condition filtering on the year and s.yearID = 1985, you may want to change it using the keyword BETWEEN or removing it altogether depending of your need.
select cast(tf.franchname as char(20)), s.yearID, s.lgid, avg(s.salary)
from salaries s, teams t, teamsfranchises tf
where s.teamID = t.teamID and
t.franchID = tf.franchID and
(s.yearID between 1985 and 2014 )and
(s.lgid='AL' OR s.lgid='NL')
group by tf.franchname, s.yearID, s.lgid
order by s.yearID;
This is another view, when there is no data and still you want to get the year with zero count. Just check this link
In this you can create a temporary table which return the list of your years ie 1985 to 2015 , then just join with left outer join and see the magic.
I just get yourquery, you can replace with the accepted answer query too.
Declare #Startyear int = 1985
--1st approach to get continues year
;with yearlist as
(
select 1985 as year
union all
select yl.year + 1 as year
from yearlist yl
where yl.year + 1 <= YEAR(GetDate())
)
select year from yearlist order by year desc;
--2nd approach to get continues year
;WITH n(n) AS
(
SELECT 0
UNION ALL
SELECT n+1 FROM n WHERE n < (year(getdate()) -#Startyear)
)
SELECT year(DATEADD( YY, -n, GetDate()))
FROM n ORDER BY n
--take anyone approach and then join with your query
;with yearlist as
(
select 1985 as year
union all
select yl.year + 1 as year
from yearlist yl
where yl.year + 1 <= YEAR(GetDate())
)
select year from yearlist
left join
(
SELECT CAST(tf.franchname AS CHAR(20)), s.yearID, s.lgid, AVG(s.salary)
FROM salaries s, teams t, teamsfranchises tf
WHERE s.teamID = t.teamID AND
t.franchID = tf.franchID AND
s.yearID = 1985 AND
(s.lgid='AL' OR s.lgid='NL') GROUP BY tf.franchname, s.yearID, s.lgid order BY
s.yearID
) yourtable on yourtable.yearID = yearlist.year
order by year desc;

Joining multiple sum sql statements

How can I join multiple sql statements. I need to display in one view respectively 2004,2008,2009 and so on
SELECT SUM(ACQUISITIONPRICE) AS YEAR_2007
FROM TRANS
WHERE DATEACQUIRED LIKE '%07';
SELECT SUM(ACQUISITIONPRICE) AS YEAR_2008
FROM TRANS
WHERE DATEACQUIRED LIKE '%08';
SELECT SUM(ACQUISITIONPRICE) AS YEAR_2009
FROM TRANS
WHERE DATEACQUIRED LIKE '%09';
check this sql fiddle
I have handled your requirement with case statement which seems to be very easy.
SELECT SUM(ACQUISITIONPRICE) AS TOTALPRICE
, EXTRACT(year from DATEACQUIRED) AS YEAR
FROM TRANS
GROUP BY EXTRACT(year from DATEACQUIRED)
ORDER BY YEAR;
SELECT Sum(CASE
WHEN Extract(year FROM dateacquired) = 2007 THEN acquisitionprice
ELSE 0
END) AS YEAR_2007,
Sum(CASE
WHEN Extract(year FROM dateacquired) = 2008 THEN acquisitionprice
ELSE 0
END) AS YEAR_2008,
Sum(CASE
WHEN Extract(year FROM dateacquired) = 2009 THEN acquisitionprice
ELSE 0
END) AS YEAR_2009
FROM trans
Just use GROUP BY like so:
SELECT SUM(ACQUISITIONPRICE) AS TOTALPRICE
, EXTRACT(year from DATEACQUIRED) AS YEAR
FROM TRANS
GROUP BY EXTRACT(year from DATEACQUIRED)
ORDER BY YEAR
If you really want to "join" them all in one row, with specific column names, you can do the following (though this approach is less flexible, since the query will have to be manually modified every year). Also note that although the question specifically mentioned "joining" the results, the join as shown below is completely unnecessary. Each column could simply be part of the select clause without any join. (here is the SqlFiddle)
SELECT * FROM
(SELECT SUM(ACQUISITIONPRICE) AS YEAR_2007
FROM TRANS
WHERE EXTRACT(year from DATEACQUIRED) = 2007) a
JOIN
(SELECT SUM(ACQUISITIONPRICE) AS YEAR_2008
FROM TRANS
WHERE EXTRACT(year from DATEACQUIRED) = 2008) b ON 1=1
JOIN
(SELECT SUM(ACQUISITIONPRICE) AS YEAR_2009
FROM TRANS
WHERE EXTRACT(year from DATEACQUIRED) = 2009) c ON 1=1
If you want the results in a single row, a preferable approach is to use a sub-select with dual as shown in this fiddle:
SELECT
(SELECT SUM(ACQUISITIONPRICE) FROM TRANS WHERE EXTRACT(year from DATEACQUIRED) = 2007) AS YEAR_2007
, (SELECT SUM(ACQUISITIONPRICE) FROM TRANS WHERE EXTRACT(year from DATEACQUIRED) = 2008) AS YEAR_2008
, (SELECT SUM(ACQUISITIONPRICE) FROM TRANS WHERE EXTRACT(year from DATEACQUIRED) = 2009) AS YEAR_2009
FROM dual
When it comes to performance, the only real way to be sure is of course to test it in your particular scenario, but the SUM (CASE... approach shown by #Indra-Prakash-Tiwari may perform better in most cases (if oracle behaves anything like sql server)
Try Use To_Char
Fiddle demo
select TO_CHAR(DATEACQUIRED,'yy') AS Years_Char,Sum(ACQUISITIONPRICE)
FROM trans GROUP BY TO_CHAR(DATEACQUIRED,'yy')