How i can i subtract two selected columns in the same query? - sql

select (select count(*) from tbl_account) as processed,
(select count(*) from tbl_rejected_account) as rejected,
processed -rejected as approved from tbl_control .
how can i get that 'approved' count without having to write two subqueries and subtract them later.
EDIT:
The original Query that i want to change:-
select
ACTIVITY_DATE
,SYSTEM_NAME
,START_TIME
,END_TIME
,INSTANCE_NO as instance_number
,case status when '1' then 'Success'
when '2' then 'In process'
when '3' then 'Failed' end as status
,(select count(distinct account_number) from tbl_account_detail where coretype=a.system_name and INSTANCE_NO=a.instance_no and a.activity_date=to_char(upload_date,'dd-MON-yy'))+
(select count(distinct account_number) from tbl_account_detail_exception where system_name=a.system_name and INSTANCE_NO=a.instance_no and a.activity_date=to_char(upload_date,'dd-MON-yy'))
as AccountCount
,(select count(distinct account_number) from tbl_account_detail where CREATOR='SYSTEM' and APPROVER='SYSTEM' and system_name=a.system_name and INSTANCE_NO=a.instance_no and a.activity_date=to_char(upload_date,'dd-MON-yy'))
as AutoApprovedCount
,(select count(distinct account_number) from tbl_account_detail where coretype=a.system_name and INSTANCE_NO=a.instance_no and a.activity_date=to_char(upload_date,'dd-MON-yy')) +
(select count(distinct account_number) from tbl_account_detail_exception where system_name=a.system_name and INSTANCE_NO=a.instance_no and
a.activity_date=to_char(upload_date,'dd-MON-yy')) -
(select count(distinct account_number) from tbl_account_detail where a.activity_date=to_char(upload_date,'dd-MON-yy') and CREATOR='SYSTEM' and APPROVER='SYSTEM' and system_name=a.system_name and INSTANCE_NO=a.instance_no)
as MaintenanceCount
from tbl_control_file_status a where activity_type='MAIN' and activity_name='START';
clearly this is not what should be the proper way,kindly provide an alternative solution.

You can use a subquery to introduce aliases for use in the outer query:
select SubQueryAlias.*
, processed - rejected as approved
from (
select (
select count(*)
from tbl_account
) as processed,
(
select count(*)
from tbl_rejected_account
) as rejected
from dual
) as SubQueryAlias
;
It's often more readable to use a common-table expression (CTE) as in Alex Poole's answer.

You can't refer to a column alias in the same level of query it is defined, except in an order by clause. That is because of the way the query is processed and the result set constructed, but also avoids ambiguity. This is mentioned in the documentatin:
Specify an alias for the column expression. Oracle Database will use this alias in the column heading of the result set. The AS keyword is optional. The alias effectively renames the select list item for the duration of the query. The alias can be used in the order_by_clause but not other clauses in the query.
You could use a CTE for each subquery, or inline views:
select ta.processed, tra.rejected, ta.processed - tra.rejected as approved
from (
select count(*) as processed from tbl_account
) ta
cross join (
select count(*) as rejected from tbl_rejected_account
) tra
Or if you really have a correlation with the third table:
select tc.id, ta.processed, tra.rejected, ta.processed - tra.rejected as approved
from tbl_control tc
join (
select id, count(*) as processed from tbl_account group by id
) ta on ta.id = tc.id
join (
select id, count(*) as rejected from tbl_rejected_account group by id
) tra on tra.id = tc.id
You haven't said what the relationship is so I've assumed a common ID column. Using CTEs rather than inline views that would look like:
with ta as (
select id, count(*) as processed from tbl_account group by id
), tra as (
select id, count(*) as rejected from tbl_rejected_account group by id
)
select tc.id, ta.processed, tra.rejected, ta.processed - tra.rejected as approved
from tbl_control tc
join ta on ta.id = tc.id
join tra on tra.id = tc.id
You may need outer joins and nvl if either subquery table might not have matching rows.
You don't really need to use subqueries, inline views or CTEs here though; you can just join the tables and have the aggregates in the top-level query - you need to duplicate the count not the whole subquery:
with ta as (
select id, count(*) as processed from tbl_account group by id
), tra as (
select id, count(*) as rejected from tbl_rejected_account group by id
)
select tc.id, count(ta.id) as processed,
count(tra.id) as rejected,
count(ta.id) - count(tra.id) as approved
from tbl_control tc
join tbl_approved ta on ta.id = tc.id
join tbl_rejected tra on tra.id = tc.id
group by tc.id
You can add more joins and conditions as needed of course.

Related

How to use DISTINCT ON but ORDER BY another expression?

The model Subscription has_many SubscriptionCart.
A SubscriptionCart has a status and an authorized_at date.
I need to pick the cart with the oldest authorized_at date from all the carts associated to a Subscription, and then I have to order all the returned Subscription results by this subscription_carts.authorized_at column.
The query below is working but I can't figure out how to select DISTINCT ON subscription.id to avoid duplicates but ORDER BY subscription_carts.authorized_at .
raw sql query so far:
select distinct on (s.id) s.id as subscription_id, subscription_carts.authorized_at, s.*
from subscriptions s
join subscription_carts subscription_carts on subscription_carts.subscription_id = s.id
and subscription_carts.plan_id = s.plan_id
where subscription_carts.status = 'processed'
and s.status IN ('authorized','in_trial', 'paused')
order by s.id, subscription_carts.authorized_at
If I try to ORDER BY subscription_carts.authorized_at first, I get an error because the DISTINCT ON and ORDER BY expressions must be in the same order.
The solutions I've found seem too complicated for what I need and I've failed to implement them because I don't understand them fully.
Would it be better to GROUP BY subscription_id and then pick from that group instead of using DISTINCT ON? Any help appreciated.
This requirement is necessary to make DISTINCT ON work; to change the final order, you can add an outer query with another ORDER BY clause:
SELECT *
FROM (SELECT DISTINCT ON (s.id)
s.id as subscription_id, subscription_carts.authorized_at, s.*
FROM subscriptions s
JOIN ...
WHERE ...
ORDER BY s.id, subscription_carts.authorized_at
) AS subq
ORDER BY authorized_at;
You don't have to use DISTINCT ON. While it is occasionally useful, I personally find window function based approaches much more clear:
-- Optionally, list all columns explicitly, to remove the rn column again
SELECT *
FROM (
SELECT
s.id AS subscription_id,
c.authorized_at,
s.*,
ROW_NUMBER () OVER (PARTITION BY s.id ORDER BY c.authorized_at) rn
FROM subscriptions s
JOIN subscription_carts c
ON c.subscription_id = s.id
AND c.plan_id = s.plan_id
WHERE c.status = 'processed'
AND s.status IN ('authorized', 'in_trial', 'paused')
) t
WHERE rn = 1
ORDER BY subscription_id, authorized_at

How many types of SQL subqueries are there?

In an effort to understand what types of subqueries can be correlated I wrote the SQL query shown below. It shows all types of subqueries I can think of a SQL select statement can include.
Though the example shown below runs in Oracle 12c, I would prefer to keep this question database agnostic. In the example below I included all 7 types of subqueries I can think of:
with
daily_login as ( -- 1. Independent CTE [XN]
select user_id, trunc(login_time) as day, count(*) from shopper_login
group by user_id, trunc(login_time)
),
frequent_user as ( -- 2. Dependent CTE [XN]
select user_id, count(*) as days from daily_login group by user_id
having count(*) >= 2
),
referrer (frequent_id, id, rid, ref_level) as ( -- 3. Recursive CTE [XN]
select fu.user_id, s.id, s.ref_id, 1 from frequent_user fu
join shopper s on fu.user_id = s.id
union all
select r.frequent_id, s.id, s.ref_id, r.ref_level + 1 from referrer r
join shopper s on s.id = r.rid
)
select s.id, s.name, r.id as original_referrer,
( -- 4. Scalar Subquery [CN]
select max(login_time) from shopper_login l
where l.user_id = s.id and l.success = 1
) as last_login,
m.first_login
from shopper s
join referrer r on r.frequent_id = s.id
join ( -- 5. Table Expression / Inline View / Derived Table [XN]
select user_id, min(login_time) first_login from shopper_login
where success = 1 group by user_id
) m on m.user_id = s.id
where r.rid is null
and s.id not in ( -- 6. Traditional Subquery [CN]
select user_id from persona
where description = 'Fashionista'
and id in ( -- 7. Nested subquery [CN]
select user_id from users where region = 'NORTH')
);
Legend:
[C]: Can be correlated
[X]: Cannot be corretaled
[N]: Can include nested subqueries
My questions are:
Did I get all possible types? Are there alternative names for these types of subqueries?
Am I correct thinking that only Scalar (#4), Traditional (#6), and Nested (#7) subqueries can be correlated?
Am I correct thinking Table Expressions and CTEs (#1, #2, and #3) cannot be correlated? (however, they can include Nested subqueries that can be correlated)
Correlated subquery:
FROM shopper s
...
AND EXISTS (SELECT *
FROM otherTable t
WHERE t.id = s.id)

How to fetch all rows from table with count of specific group?

I have a simple table like this
spatialite> select id, group_id, object_id, object, param from controlled_object;
1|1|150|nodes|0.5
2|1|186|nodes|0.5
3|2|372|nodes|1.0
The second column is group_id. I want to retrieve all entries from the table, plus the count of the group.
1|1|150|nodes|0.5|2
2|1|186|nodes|0.5|2
3|2|372|nodes|1.0|1
I thought a cross join would be the way to go
SELECT
*
, cj.cnt
FROM
controlled_object
CROSS JOIN (
SELECT
COUNT(DISTINCT group_id) AS cnt
FROM
controlled_object
) AS cj
But that gives me
1|1|150|nodes|0.5|2|2
2|1|186|nodes|0.5|2|2
3|2|372|nodes|1.0|2|2
How do I fetch all rows from table including the count of a specific group?
Join source data with counters, grouped by group_id
select c.id, c.group_id, c.object_id, c.object, c.param,cnt from controlled_object c join
(select group_id,count(*) cnt from controlled_object group by group_id) p on c.group_id =p.group_id ;
Not very good idea for big tables
Sqlite is not very good idea for big tables at all :-)
You can compute the count with a correlated subquery:
SELECT id,
group_id,
object_id,
object,
param,
(SELECT count(*)
FROM controlled_object AS co2
WHERE group_id = controlled_object.group_id)
FROM controlled_object;

trouble with a correlated subquery, inner query cannot find column

i have been having for SQL trouble, i am using an SQLite database and can sadly not get the following to work. All help is appreciated.
I want the following query to sum all integers in the amount column of transactionTable fitting the criteria. One of the criteria changes from row to row in the outer query, so i want the inner query to run one time for every row in the outer query.
i get the following error when running this query: "no such column: ct.name"
i have bolded the line that i think is wrong in the query below.
table 1 : categoryTable
columns: id, icon, name, starred
table 2 : transactionTable
columns: id, date, amount, sign, category
query:
SELECT id, icon, name, starred, mySum
FROM categoryTable ct,
(SELECT sum(amount) AS mySum FROM transactionTable
WHERE date<'1992' AND date>'1990'
AND sign = '-' AND category=ct.name) AS sumTable
WHERE mySum!=0
ORDER BY mySum DESC
Thanks!
I think this can be done without the subquery. Try this version.
SELECT ct.id, ct.icon, ct.name, ct.starred, SUM(tt.amount) AS transactionSum
FROM categoryTable ct
INNER JOIN transactionTable tt
ON ct.name = tt.category
AND tt.date < '1992'
AND tt.date > '1990'
AND tt.sign = '-'
GROUP BY ct.id, ct.icon, ct.name, ct.starred
HAVING SUM(tt.amount) != 0
ORDER BY transactionSum DESC

MS-SQL problem - Only one expression can be specified in the select list

I write the following query:
select
id,
(select NameEn from [Campaign] where id=CampaignId) as CampaignName,
createdDate,
(select Name, IdNo, Email, MobileNo from [Members] where id=MemberId)
from
Transactions
and error occurs:
"Only one expression can be specified in the select list when the subquery is not introduced with EXISTS."
How can I rewrite the SQL to make it correct?
You need to use proper (inner|left|...) join syntax.
Something like:
select
t.id,
c.NameEn,
t.createdDate,
m.Name,
m.IdNo,
m.Email,
m.MobileNo
from
[Transactions] t
inner join [Campaign] c on c.id = t.CampaignId
inner join [Members] m on m.id = t.MemberId
Also, in your original code, one of
select NameEn from [Campaign] where id=CampaignId
or
select Name,IdNo,Email,MobileNo from [Members] where id=MemberId
might be returning more than one row for each row of [Transactions], which would be illegal.