How to use with statement in this query? - plsql

In this query, I want to use with-statement. I have a subquery that calculates A union all B and I want to use it with with-statement. But when I use with-statement I face the error that says "table or view does not exist".
what surprises me is when I replace the first part with with-statement it works correctly. But when I replace the second part, I face this error!!
select
deposit.BRNCH_COD||'-'||deposit.DP_TYPE_COD||''||deposit.CUSTOMER_NUM||'-
'||deposit.DEPOSIT_SERIAL AS DEPOSIT_NUMBER,
deposit.IBAN AS IBAN,
deposit.CURRENCY_DESC AS DEPOSIT_CURRCOD,
deposit.BRNCH_COD AS BRNCH_COD,
MAIN_7.Still_Days
AS Still_Lenght,
to_char(MAIN_7.Startdate, 'yyyy/mm/dd' ,'nls_calendar=persian') AS
START_DATE,
MAIN_7.AMOUNT
AS TOTAL_AMOUNT,
MAIN_7.TRN_Count
AS TRN_Count
from
(
select Trans_Table.DEPOSIT_KEY AS DEPOSIT_KEY,
Trans_Table.TRN_Start_DATE AS Startdate,
MAX(Active_Time_Table.EFFECTIVE_DATE) AS Lastdate,
H.PASSIVE_DAYS AS Still_Days,
SUM(Active_Time_Table.AMOUNT) AS AMOUNT,
Count(Active_Time_Table.AMOUNT) AS TRN_Count
from
(
Select F.DEPOSIT_KEY,
SUM (F.AMOUNT) AS TRN_AMOUNT,
MIN (F.EFFECTIVE_DATE) AS TRN_Start_DATE
from
(
A
union all
B
)F
Group by (F.DEPOSIT_KEY)
Having ( SUM (F.AMOUNT) >10000000000)
)Trans_Table
inner join
H
on (Trans_Table.DEPOSIT_KEY = H.DEPOSIT_KEY and
Trans_Table.TRN_Start_DATE-1 = H.EFFECTIVE_DATE)
inner join
(
A
union all
B
)Active_Time_Table
on (Trans_Table.DEPOSIT_KEY = Active_Time_Table.DEPOSIT_KEY and
Active_Time_Table.EFFECTIVE_DATE - Trans_Table.TRN_Start_DATE< 4 and
Active_Time_Table.EFFECTIVE_DATE - Trans_Table.TRN_Start_DATE>=0)
group by ( Trans_Table.DEPOSIT_KEY ,
Trans_Table.TRN_Start_DATE,H.PASSIVE_DAYS)
Having (SUM(Active_Time_Table.AMOUNT)) > 10000000000
)MAIN_7
inner join dimamldeposit deposit
on deposit.DEPOSIT_KEY = MAIN_7.DEPOSIT_KEY
***********************************************************
with rep as
(A union all B)
select
deposit.BRNCH_COD||'-'||deposit.DP_TYPE_COD||'-
'||deposit.CUSTOMER_NUM||'-'||deposit.DEPOSIT_SERIAL AS DEPOSIT_NUMBER,
deposit.IBAN AS IBAN,
deposit.CURRENCY_DESC AS DEPOSIT_CURRCOD,
deposit.BRNCH_COD AS BRNCH_COD,
MAIN_7.Still_Days AS Still_Lenght,
to_char(MAIN_7.Startdate, 'yyyy/mm/dd' ,'nls_calendar=persian') AS START_DATE,
MAIN_7.AMOUNT AS TOTAL_AMOUNT,
MAIN_7.TRN_Count AS TRN_Count
from
(
select Trans_Table.DEPOSIT_KEY AS DEPOSIT_KEY,
Trans_Table.TRN_Start_DATE AS Startdate,
MAX(rep.EFFECTIVE_DATE) AS Lastdate,
H.PASSIVE_DAYS AS Still_Days,
SUM(rep.AMOUNT) AS AMOUNT,
Count(rep.AMOUNT) AS TRN_Count
from
(
Select rep.DEPOSIT_KEY,
SUM (rep.AMOUNT) AS TRN_AMOUNT,
MIN (rep.EFFECTIVE_DATE) AS TRN_Start_DATE
from
rep
Group by (rep.DEPOSIT_KEY)
Having ( SUM (rep.AMOUNT) >10000000000)
)Trans_Table
inner join
H
on (Trans_Table.DEPOSIT_KEY = H.DEPOSIT_KEY and Trans_Table.TRN_Start_DATE-1 = H.EFFECTIVE_DATE)
inner join
rep rep
on (Trans_Table.DEPOSIT_KEY = rep.DEPOSIT_KEY and rep.EFFECTIVE_DATE - Trans_Table.TRN_Start_DATE< 4 and rep.EFFECTIVE_DATE - Trans_Table.TRN_Start_DATE>=0)
group by ( Trans_Table.DEPOSIT_KEY , Trans_Table.TRN_Start_DATE,H.PASSIVE_DAYS)
Having (SUM(rep.AMOUNT)) > 10000000000
)MAIN_7
inner join dimamldeposit deposit
on deposit.DEPOSIT_KEY = MAIN_7.DEPOSIT_KEY

That's a lot of code, but - to make it simple, I'd suggest you use WITH factoring clause as the first command, include all tables you use into it, and then - as the final SELECT - fetch data from all those CTEs. Something like this:
with
a as (select ... from ...),
b as (select ... from ...),
f as (select ... from ...),
...
select a.col1, b.col2, f.col3
from a join b on a.id = b.id
left join f on f.id = b.id
where ...

Related

How to select max() value of some of the selected rows?

There are two types of rows in document_files table - that have active=1 or active=0. Each document (document_id) can have multiple rows of each type. The goal is to for every document select all rows that have active=1, but only the latest one of all rows that have active=0.
select *
from documents d
inner join document_files df on df.document_id = d.id
where ...
-- df.active = 1, select them all
-- or df.active = 0, only select max(df.last_stamp) of all df.active=0 rows for this df.document_id
How to achieve this?
I'd use union and group by, but the problem is that a lot of columns are returned, and it wouldn't be optimal to group them all.
This section only get the document files with no active record and after that using max () gets the most recent record for the item:
select document_id
from (select * from document_files f where f.document_id not in
(select document_id from document_files where active = 1)) dff where dff.last_stamp =
(
select max(i.last_stamp) from document_files i where i.document_id = dff.document_id
)
My final sql :
select *
from documents d
inner join document_files df on df.document_id = d.id
where df.active = 1 or
df.document_id in
(
select document_id
from (select * from document_files f where f.document_id not in
(select document_id from document_files where active = 1)) dff where dff.last_stamp =
(
select max(i.last_stamp) from document_files i where i.document_id = dff.document_id
)
)
Please add some sql fiddle next time so we can test the result

How do I include all max values within a row?

I'm very new to learning SQL, I apologize if my question isn't completely accurate.
The question I'm trying to answer with this query is "What is the most popular music genre in each country?" I've had to use a subquery and it works, but I found that for a few countries in the table, more than one genre has the MAX value. I'm stuck with how to edit my query so that all genres with the max value show in the results. Here is my code, using DB Browser for SQLite:
SELECT BillingCountry AS Country , name AS Genre , MAX(genre_count) AS Purchases
FROM (
SELECT i.BillingCountry, g.name, COUNT(g.genreid) AS genre_count
FROM Invoice i
JOIN InvoiceLine il
ON il.InvoiceId = i.InvoiceId
JOIN TRACK t
ON il.trackid = t.TrackId
JOIN Genre g
ON t.genreid = g.GenreId
GROUP BY 1,2
) sub
GROUP BY 1
Here is an example of the result:
| Country | Genre |Purchase|
|---------|-------|--------|
|Agrentina| Punk | 9 |
|Australia| Rock | 22 |
BUT in running just the subquery to COUNT the purchases, Argentina has two Genres with 9 Purchases (the max number for that country). How do I adjust my query to include both and not just the first one in the row?
You can do it with RANK() window function:
SELECT BillingCountry, name, genre_count
FROM (
SELECT i.BillingCountry, g.name, COUNT(*) AS genre_count,
RANK() OVER (PARTITION BY i.BillingCountry ORDER BY COUNT(*) DESC) rnk
FROM Invoice i
INNER JOIN InvoiceLine il ON il.InvoiceId = i.InvoiceId
INNER JOIN TRACK t ON il.trackid = t.TrackId
INNER JOIN Genre g ON t.genreid = g.GenreId
GROUP BY i.BillingCountry, g.name
)
WHERE rnk = 1
This will return the ties in separate rows.
If you want 1 row for each country, you could also use GROUP_CONCAT():
SELECT BillingCountry, GROUP_CONCAT(name) AS name, MAX(genre_count) AS genre_count
FROM (
SELECT i.BillingCountry, g.name, COUNT(*) AS genre_count,
RANK() OVER (PARTITION BY i.BillingCountry ORDER BY COUNT(*) DESC) rnk
FROM Invoice i
INNER JOIN InvoiceLine il ON il.InvoiceId = i.InvoiceId
INNER JOIN TRACK t ON il.trackid = t.TrackId
INNER JOIN Genre g ON t.genreid = g.GenreId
GROUP BY i.BillingCountry, g.name
)
WHERE rnk = 1
GROUP BY BillingCountry

How to select gaps in my sqlite database

I'm trying to write down a select to show me all the items that do not exist in my database. Say I have a table named TBL795 with a column named NRBEM which could not have any gaps in it.
Should be this way:
Those numbers are in sequence.
If they looked like this:
they would be wrong, because some items have not being inserted into the table.
In a table with thousand of items it would be very difficult to find out if there are any gaps and which are the missing items.
One solution would be this:
CREATE TABLE TESTE (
NRBEM VARCHAR(15))
feed it with a command like this:
INSERT INTO TESTE
WITH RECURSIVE
cnt(NRBEM) AS (VALUES(1) UNION ALL SELECT NRBEM+1 FROM cnt WHERE NRBEM <100000)
SELECT NRBEM FROM cnt A
and running this select
SELECT A.NRBEM FROM TESTE A LEFT JOIN TBL795 B
ON A.NRBEM = B.NRBEM
WHERE B.NRBEM IS NULL
I can see all the items that are missing in my table.
Since the command:
WITH RECURSIVE
cnt(NRBEM) AS (VALUES(1) UNION ALL SELECT NRBEM+1 FROM cnt WHERE NRBEM <100000)
SELECT NRBEM FROM cnt
create a virtual table I would like to run a select like this:
SELECT NRBEM FROM (
WITH RECURSIVE
cnt(NRBEM) AS (VALUES(1) UNION ALL SELECT NRBEM+1 FROM cnt WHERE NRBEM <100000)
SELECT NRBEM FROM cnt ) A LEFT JOIN TBL795 B
ON A.NRBEM = B.NRBEM
But this does not work.
This way:
SELECT X FROM (
WITH RECURSIVE
cnt(X) AS (VALUES(1) UNION ALL SELECT X+1 FROM cnt WHERE X <100000)
SELECT X FROM cnt ) A LEFT JOIN TBL795 B
ON A.X = B.NRBEM
it works, but does not select the right items.
So, how could I write this select?
It would be possible to use an outer join and filter out matches, but using set operations is simpler:
WITH RECURSIVE CNT(NRBEM) AS (...)
SELECT NRBEM
FROM CNT
WHERE NRBEM NOT IN (SELECT NRBEM
FROM tbl795);
I found out what I was doing wrong.
If I cast nrbem as number the select works.
SELECT A.X FROM (
WITH RECURSIVE
cnt(X) AS (VALUES(1) UNION ALL SELECT X+1 FROM cnt WHERE X <100000)
SELECT X FROM cnt ) A LEFT JOIN ( SELECT CAST( NRBEM AS NUMBER ) AS NRBEM FROM TBL795 ) B
ON A.X = B.NRBEM
WHERE B.NRBEM IS NULL
If I want to check the range from item 2400 to 2700 to see if there are any gaps I can do this:
SELECT A.X FROM (
WITH RECURSIVE
cnt(X) AS (VALUES(2400) UNION ALL SELECT X+1 FROM cnt WHERE X < (2700))
SELECT X FROM cnt ) A LEFT JOIN ( SELECT CAST( NRBEM AS NUMBER ) AS NRBEM FROM TBL795 WHERE NRBEM >= 2400 and nrbem <= 2700 ) B
ON A.X = B.NRBEM
WHERE B.NRBEM IS NULL
LIMIT ( 2700 - 2400 + 1 )

What package is ordered most often at each location?

I have been having trouble finding the result for this problem.
Below are my table structure details:
studios
(
studio_id,
location
)
photo_sessions
(
session_id,
studio_id,
cust_id,
session_length,
session_date
)
customer
(
cust_id,
cus_first,
cus_last,
emailid
)
order
(
order_id,
cust_id,
order_description,
amount
)
package_order
(
package_order_id,
order_id,
package_id,
price,
quantity
)
package
(
package_id,
package_name
)
I would like to find which package is ordered most often at each location.
I tried this query,
SELECT
p.package_name,
s.location,
count(p.package_id)
FROM package p,
package_order po,
order_table o,
customer c,
photo_sessions ps,
studios s
WHERE p.package_id=po.package_id
and po.order_id=o.order_id
and o.cust_id=c.cust_id
and c.cust_id=ps.cust_id
and ps.studio_id=s.studio_id
GROUP BY p.package_name,
s.location
HAVING COUNT (p.package_id)=
(
SELECT MAX(mycount)
FROM (
SELECT package_id, COUNT(package_id) as mycount
FROM package_order
GROUP BY package_id
) as most_order
);
I am not sure it gives the right result. Any help will be appreciated.
You can do this with window functions:
WITH cte as (
SELECT
p.package_name,
s.location,
count(p.package_id) AS mycount,
row_number() over(partition by s.location order by count(p.package_id) desc) AS rn
FROM package p
JOIN package_order po ON p.package_id = po.package_id
JOIN order_table o ON po.order_id = o.order_id
JOIN customer c ON o.cust_id = c.cust_id
JOIN photo_sessions ps ON c.cust_id = ps.cust_id
JOIN studios s ON ps.studio_id = s.studio_id
GROUP BY p.package_name,
s.location)
SELECT * FROM cte
WHERE rn = 1
PS: you use Sql Server 2012 but joining with old style deprecated syntax. It is time to forget about old one.
EDIT:
WITH cte as (
SELECT
p.package_name,
s.location,
count(p.package_id) AS mycount
FROM package p
JOIN package_order po ON p.package_id = po.package_id
JOIN order_table o ON po.order_id = o.order_id
JOIN customer c ON o.cust_id = c.cust_id
JOIN photo_sessions ps ON c.cust_id = ps.cust_id
JOIN studios s ON ps.studio_id = s.studio_id
GROUP BY p.package_name,
s.location)
SELECT * FROM cte c1
CROSS APPLY(SELECT MAX(mycount) mycount FROM cte c2 WHERE c1.location = c2.location) ca
WHERE c1.mycount = ca.mycount

Inner Join & Count, Microsoft SQL

I am trying to do a count inside a nested statement with inner join
select a.app_id, a.first_name, a.last_name, d.svd_id
from wwhs_app a inner join
wwhs_svc d on a.app_id = d.app_id
where a.app_id in(
select top 50 app_id
from wwhs_app
Where app_create_dt > '2012-07-23 00:00:00')
I need a count of svd_id as well, but I keep getting errors every way I try. Suggestions?
You need to count for svd_id but it's not in the query.
Did you mean 'app_id'?
Try this...
SELECT a.app_id, a.first_name, a.last_name, d.svd_id
FROM wwhs_app a
INNER JOIN wwhs_svc d on a.app_id = d.app_id
WHERE a.app_id in (
SELECT TOP 50 app_id, COUNT(*) as id_count
FROM wwhs_app
WHERE app_create_dt > '2012-07-23 00:00:00'
GROUP BY app_id ORDER BY id_count)

Resources