I need 2 count columns in the same query in ORACLE - oracle

I'm trying to get the unique number of invoices a company has received and sent out using 2 count() functions. In invoices table there are two columns that are references to the same company id (one is id of a company that is sending an invoice and the other one is id of a company that is receiving an invoice)
This is the code I tried using:
SELECT K.ID,K.NAME,K.CITY, COUNT(*) AS NUM_OF_INVOICES_SENT, COUNT(*) AS NUM_OF_INVOICES_RECEIVED
FROM COMPANY K LEFT JOIN INVOICE F ON F.COMP_SNEDING = K.ID
GROUP BY K.NAME,K.ID,K.CITY
This is for a school project so I am in no means well versed in sql/oracle
actual data invoices:
actual data company:
desired outcome with given actual data:

Here's one option; it doesn't use count, but sum with case expression.
Sample data:
SQL> with
2 invoice (id, amount, comp_sending, comp_receiving) as
3 (select 1, 2000 , 1, 2 from dual union all
4 select 2, 28250, 3, 2 from dual union all
5 select 3, 8700 , 4, 1 from dual union all
6 select 4, 20200, 5, 3 from dual union all
7 select 5, 21500, 3, 4 from dual
8 ),
9 company (id, name, city, state) as
10 (select 1, 'Microsoft', 'Redmond' , 'Washington' from dual union all
11 select 2, 'Ubisoft' , 'Paris' , 'France' from dual union all
12 select 4, 'Starbucks', 'Seattle' , 'Washington' from dual union all
13 select 5, 'Apple' , 'Cupertino', 'California' from dual union all
14 select 3, 'Nvidia' , 'Cupertino', 'California' from dual
15 )
Query begins here:
16 select c.id, c.name,
17 sum(case when c.id = i.comp_sending then 1 else 0 end) cnt_sent,
18 sum(case when c.id = i.comp_receiving then 1 else 0 end) cnt_received
19 from company c left join invoice i on c.id in (i.comp_sending, i.comp_receiving)
20 group by c.id, c.name
21 order by c.id;
ID NAME CNT_SENT CNT_RECEIVED
---------- --------- ---------- ------------
1 Microsoft 1 1
2 Ubisoft 0 2
3 Nvidia 2 1
4 Starbucks 1 1
5 Apple 1 0
SQL>

You can use COUNT if you replace the 0 in the CASE expressions with NULL. So #Littlefoot's query becomes
select c.id, c.name,
COUNT(case when c.id = i.comp_sending then 1 else NULL end) cnt_sent,
COUNT(case when c.id = i.comp_receiving then 1 else NULL end) cnt_received
from company c left join invoice i on c.id in (i.comp_sending, i.comp_receiving)
group by c.id, c.name
order by c.id;
This works because COUNT counts only those rows which have a non-NULL value in the expression which is being counted.
db<>fiddle here

Related

single result for 2 queries ORACLE

I have a POST table, a CATEGORY table, a ACTION table and ACTION_TYPE table, I explain the ACTION table contains all the actions that were made, and the table ACTION_TYPE contains the actions details for example the ACTION whose ID = 4 has ACTION_TYPE_ID = 1 for POST_ID 6, which mean an action was made for post number 50, we can have many actions for one post_id
The POST table
id title content category_id
---------- ---------- ---------- ------------
1 title1 Text... 1
2 title2 Text... 1
3 title3 Text... 1
4 title4 Text... 3
5 title5 Text... 2
6 title6 Text... 1
The CATEGORY table
id name
---------- ----------
1 category_1
2 category_2
3 category_3
The ACTION_TYPE table
id name
---------- ----------
1 updated
2 deleted
3 restored
4 hided
The ACTION table
id post_id action_type_id date
---------- ---------- -------------- -----
1 1 1 2017-01-01
2 1 1 2017-02-15
3 1 3 2018-06-10
4 6 1 2019-08-01
5 5 2 2019-12-09
6 2 3 2020-04-27
7 2 1 2020-07-29
8 3 2 2021-03-13
Now I explain the case, I actually have two queries a query to count the posts for each category and another to count the actions performed on each post by category which work perfectly.
Here is my first query
select categories, count(*) as cnt_posts_per_cat
from(
select
case
when p.category_id is not null then c.name
end as categories
from post p
left join category c on p.category _id = c.id
)
group by categories
;
Which brings this result
categories cnt_posts_per_cat
---------- -------------------
category_1 4
category_2 1
category_3 1
Ans here is my second query
select categories, count(*) as cnt_actions_per_cat
from(
select distinct ac.post_id AS action_post_id, max(ac.date) over (partition by ac.post_id) as max_date,
case
when ac.action_type_id is not null then act.name
end as actions,
case
when p.category_id is not null then c.name
else 'na'
end as categories
from action ac
left join post p on ac.post_id = p.id
left join category c on p.category _id = c.id
left join action_type act on ac.action_type_id = act.id
where act.name in ('restored','deleted','updated')
)
group by categories
;
Which brings this correct result because it's seclect the last action for each action_type
categories cnt_actions_per_cat
---------- -------------------
category_1 3
category_2 1
category_3 na
But I would like to have a single result table for both queries at the same time as follow :
Here the result expected to be
categories cnt_posts_per_cat cnt_actions_per_cat
---------- ----------------- -------------------
category_1 4 3
category_2 1 1
category_3 1 na
i was trying union and union all but it's not correct it return following result
categories cnt_posts_per_cat
---------- -----------------
category_1 7
category_2 2
category_3 1
Best regards
How about correlated subqueries?
Sample data:
SQL> with
2 post (id, category_id) as
3 (select 1, 1 from dual union all
4 select 2, 1 from dual union all
5 select 3, 1 from dual union all
6 select 4, 3 from dual union all
7 select 5, 2 from dual union all
8 select 6, 1 from dual
9 ),
10 category (id, name) as
11 (select 1, 'category_1' from dual union all
12 select 2, 'category_2' from dual union all
13 select 3, 'category_3' from dual
14 ),
15 action_type (id, name) as
16 (select 1, 'updated' from dual union all
17 select 2, 'deleted' from dual union all
18 select 3, 'restored' from dual union all
19 select 4, 'hided' from dual
20 ),
21 action (id, post_id, action_type_id) as
22 (select 1, 1, 1 from dual union all
23 select 2, 1, 1 from dual union all
24 select 3, 1, 3 from dual union all
25 select 4, 6, 1 from dual union all
26 select 5, 5, 2 from dual union all
27 select 6, 2, 3 from dual union all
28 select 7, 2, 1 from dual union all
29 select 8, 3, 2 from dual
30 )
Query begins here:
31 select c.name,
32 --
33 (select count(*)
34 from post p
35 where p.category_id = c.id
36 ) cnt_posts_per_cat,
37 --
38 (select count(*)
39 from action a join post p on p.id = a.post_id
40 join action_type t on t.id = a.id
41 where p.category_id = c.id
42 and t.name in ('restored', 'deleted', 'updated')
43 ) cnt_actions_per_cat
44 from category c
45 order by c.name;
NAME CNT_POSTS_PER_CAT CNT_ACTIONS_PER_CAT
---------- ----------------- -------------------
category_1 4 3
category_2 1 0
category_3 1 0
SQL>

Oracle query to keep looking until value is not 0 anymore

I am using Oracle 11.
I have 2 tables
TblA with columns id, entity_id and effective_date.
TblADetail with columns id and value.
If Value = 0 for the effective date, I want to keep looking for the next effective date until I found value <> 0 anymore.
The below query only look for value on 3/10/21.
If value = 0, I want to look for value on 3/11/21. If that's not 0, I want to stop.
But, if that's 0, I want to look for value on 3/12/21. If that's not 0, I want to stop.
But, if that's 0, I want to keep looking until value is not 0.
How can I do that ?
SELECT SUM(pd.VALUE)
FROM TblA p,TblADetail pd
WHERE p.id = pd.id
AND p.effective_date = to_date('03/10/2021','MM/DD/YYYY')
AND TRIM (p.entity_id) = 123
Sample data:
TblA
id entity_id effective_date
1 123 3/10/21
2 123 3/11/21
3 123 3/12/21
TblADetail
id value
1 -136
1 136
2 2000
3 3000
In the above data, for entity_id 123, starting from effective_date 3/10/21, I would like to to return value 2000 (from TblADetail) effective_date 3/11/21.
So, starting from a certain date, I want the results from the minimum date that has non-zero values.
Thank you.
You can do what you need to do by grouping the sum on the effective date, and using the MIN analytic function to find the earliest date. Once you've done that, you simply need to select the date that matches the earliest date.
E.g.:
with tbla as (select 1 id, ' 123' entity_id, to_date('10/03/2021', 'dd/mm/yyyy') effective_date from dual union all
select 2 id, ' 123' entity_id, to_date('11/03/2021', 'dd/mm/yyyy') effective_date from dual union all
select 3 id, ' 123' entity_id, to_date('12/03/2021', 'dd/mm/yyyy') effective_date from dual),
tbla_detail as (select 1 id, -136 value from dual union all
select 1 id, 136 value from dual union all
select 2 id, 2000 value from dual union all
select 3 id, 3000 value from dual),
results as (select a.effective_date,
sum(ad.value) sum_value,
min(case when sum(ad.value) != 0 then a.effective_date end) over () min_effective_date
from tbla a
inner join tbla_detail ad on a.id = ad.id
where a.effective_date >= to_date('10/03/2021', 'dd/mm/yyyy')
and trim(a.entity_id) = '123'
group by a.effective_date)
select sum_value
from results
where effective_date = min_effective_date;
SUM_VALUE
----------
2000
Straightforward; read comments within code. Sample data in lines #1 - 13, query begins at line #14.
SQL> with
2 -- sample data
3 tbla (id, entity_id, effective_date) as
4 (select 1, 123, date '2021-03-10' from dual union all
5 select 2, 123, date '2021-03-11' from dual union all
6 select 3, 123, date '2021-03-12' from dual
7 ),
8 tblb (id, value) as
9 (select 1, -136 from dual union all
10 select 1, 136 from dual union all
11 select 2, 2000 from dual union all
12 select 3, 3000 from dual
13 ),
14 tblb_temp as
15 -- simple grouping per ID
16 (select id, sum(value) value
17 from tblb
18 group by id
19 )
20 -- return TBLA values whose ID equals TBLB_TEMP's minimum ID
21 -- whose value isn't zero
22 select a.id, a.entity_id, a.effective_date
23 from tbla a
24 where a.id = (select min(b.id)
25 from tblb_temp b
26 where b.value > 0
27 );
ID ENTITY_ID EFFECTIVE_
---------- ---------- ----------
2 123 03/11/2021
SQL>

oracle- JOIN 2 tables with 2 ID's in common

table "team1" :
id country
1 India
2 Pakistan
3 srilanka
4 England
table "team2" :
id name name2
1 2 4
2 1 3
i have to combine two tables
another table when retrieve the data that time in place of 2 , 4 Pakistan,England
It is about the self join of team1 table (lines #15 and 16):
SQL> with
2 team1 (id, country) as
3 (select 1, 'India' from dual union all
4 select 2, 'Pakistan' from dual union all
5 select 3, 'Sri Lanka' from dual union all
6 select 4, 'England' from dual
7 ),
8 team2 (id, name, name2) as
9 (select 1, 2, 4 from dual union all
10 select 2, 1, 3 from dual
11 )
12 select b.id,
13 t1.country,
14 t2.country
15 from team2 b join team1 t1 on t1.id = b.name
16 join team1 t2 on t2.id = b.name2
17 order by b.id;
ID COUNTRY COUNTRY
---------- --------- ---------
1 Pakistan England
2 India Sri Lanka
SQL>
Just showing another way of writing the same query with aggregate functions and grouping.
with
team1 (id, country) as
(select 1, 'India' from dual union all
select 2, 'Pakistan' from dual union all
select 3, 'Sri Lanka' from dual union all
select 4, 'England' from dual
),
team2 (id, name, name2) as
(select 1, 2, 4 from dual union all
select 2, 1, 3 from dual
)
SELECT
T2.ID,
MAX(CASE
WHEN T2.NAME = T1.ID THEN T1.COUNTRY
END) AS TEAM1,
MAX(CASE
WHEN T2.NAME2 = T1.ID THEN T1.COUNTRY
END) AS TEAM2
FROM
TEAM2 T2
JOIN TEAM1 T1 ON T1.ID IN (
T2.NAME,
T2.NAME2
)
GROUP BY
T2.ID
ORDER BY
T2.ID;
Output:
ID TEAM1 TEAM2
---------- --------- ---------
1 Pakistan England
2 India Sri Lanka
Cheers!!

Return Count Value back as 0 if emtpy or none

I am running the following query in our database and I am trying to get the count value back as 0 if none are present, but having problems doing so. Could someone help assist and explain how to accomplish this
select state, count(phone_number) from VOIP_PHONE_NUMBER_POOL
where status = 3
group by state
order by state asc;
For example, currently my query is returning something back like this
State Count
AZ 25
When I want it to return the empty values and look like this
State Count
AZ 25
CA 0
This mimics what you have now:
SQL> with voip_phone_number_pool (state, phone_number, status) as
2 (select 'AZ', 1234, 3 from dual union all
3 select 'AZ', 2232, 3 from dual union all
4 select 'AZ', 4444, 3 from dual union all
5 select 'AZ', 7756, 1 from dual union all
6 --
7 select 'CA', 9999, 1 from dual
8 )
9 select state,
10 count(phone_number)
11 from voip_phone_number_pool
12 where status = 3
13 group by state
14 order by state;
ST COUNT(PHONE_NUMBER)
-- -------------------
AZ 3
SQL>
There's no row for the CA state which has status = 3, so it isn't returned by that query.
What you could do, is to use outer self-join in such a manner:
SQL> with voip_phone_number_pool (state, phone_number, status) as
2 (select 'AZ', 1234, 3 from dual union all
3 select 'AZ', 2232, 3 from dual union all
4 select 'AZ', 4444, 3 from dual union all
5 select 'AZ', 7756, 1 from dual union all
6 --
7 select 'CA', 9999, 1 from dual
8 ),
9 states as
10 (select distinct state from voip_phone_number_pool)
11 select s.state,
12 count(v.phone_number)
13 from states s left join voip_phone_number_pool v on v.state = s.state
14 and v.status = 3
15 group by s.state
16 order by s.state;
ST COUNT(V.PHONE_NUMBER)
-- ---------------------
AZ 3
CA 0
SQL>
the states CTE selects all states
it is then used in outer join with voip_phone_number_pool
note that condition (status = 3) has to be moved into join (line #14); if you leave it in the where clause, you won't get desired result
If there are no rows in the fact table (VOIP_PHONE_NUMBER_POOL) with a particular state, you will not get such a result from only querying that table.
You will need to join your fact table to a dimension table (a table with all possible states) in order to get the desired counts.
For example, assuming you have such a dimension table and it is named "state_info":
SELECT s.state,
COUNT(v.phone_number) AS phone_number_count
FROM state_info s
LEFT OUTER JOIN voip_phone_number_pool v
ON s.state = v.state
GROUP BY s.state;
SELECT s.state,
COUNT(v.phone_number) AS phone_number_count
FROM CORP_ACCT s
LEFT OUTER JOIN voip_phone_number_pool v
ON s.state = v.state
GROUP BY s.state;
worked for me

Group Query for oracle

I have a scenario where I need to fetch all the records within an ID for the one source along with other source. Given below is my input set of records.
ID SOURCE CURR_FLAG TYPE
1 IBM Y P
1 IBM Y OF
1 IBM Y P
2 IBM Y P
2 TCS Y P
3 IBM NULL P
3 CTS NULL P
3 TCS NULL P
4 IBM NULL OF
4 CTS NULL OF
4 TCS Y ON
5 CTS NULL OF
5 TCS Y ON
From the above records, I need to select all the records with source as IBM within that same ID group and other source should be also there for the same ID along with IBM.Also, we need to fetch only those records where at least one record in that ID group with curr_fl='Y'
In the above scenario even though the ID=1 have a source as IBM, but there is no record in that particular group with other source.So we shouldn't fetch that record with that ID.
For the record ID=3 have a source as IBM along with other sources, but there is no record with CURR_FL='Y', my query should not fetch the value.In the case of ID=4, it can fetch all the records with ID=4, as one of the records have value='Y' and it have a combination of IBM with other source.For ID 5 it should not fetch as we dont have any IBM record source within that set
Also within the group which has satisfied the above condition, I need one more condition for type. if there are records with type='P', then I need to fetch only that record.If there are no records with P, then I will search for type='OF' else type='ON'
My Expected output is given below
ID SOURCE CURR_FLAG TYPE
2 IBM Y P
2 TCS Y P
4 IBM NULL OF
4 CTS NULL OF
4 TCS Y ON
I have written a query as given below.But it's running for long and not fetching any results. Is there any better way to modify this query
select
ID,
SOURCE,
CURR_FL,
TYPE
from TABLE a where
exists(select 1 from TABLE B where a.ID=B.ID and a.rowid<>B.rowid and B.source<>a.source)
and exists(select 1 from TABLE C where a.ID=C.ID and C.source ='IBM')
and exists(select 1 from TABLE D where a.ID=D.ID and D.CURR_FL='Y') and
(TYPE,ID) IN (
select case type when 1 then 'P' when 2 then 'OF' else 'ON' END TYPE,ID from
(select ID,
max(priority) keep (dense_rank first order by priority asc) as type
from ( select ID,TYPE,
case TYPE
when 'P' then 1
when 'OF' then 2
when 'ON' then 3
end as priority
from TABLE where ID
in(select ID from TABLE where CURR_FL='Y') AND SOURCE='IBM'
)
group by ID))
First, look for the ids. I would recommend:
select id
from t
group by id
having min(source) <> max(source) and -- at least two sources
sum(case when curr_flag = 'Y' then 1 else 0 end) > 0 and -- at least one Y
sum(case when source = 'IBM' then 1 else 0 end) > 0 -- IBM
To get the base rows, you can use in, exists, or join:
select t.*
from t
where id in (select id
from t
group by id
having min(source) <> max(source) and -- at least two sources
sum(case when curr_flag = 'Y' then 1 else 0 end) > 0 and -- at least one Y
sum(case when source = 'IBM' then 1 else 0 end) > 0 -- IBM
);
Using analytic functions you can check that there is at least one IBM in the group, at least one other source apart from IBM in the group, and at least one flag with Y in the group:
with t(id, source, curr_flag, type) as
(
select 1, 'IBM', 'Y', 'P' from dual union all
select 1, 'IBM', 'Y', 'OF' from dual union all
select 1, 'IBM', 'Y', 'P' from dual union all
select 2, 'IBM', 'Y', 'P' from dual union all
select 2, 'TCS', 'Y', 'P' from dual union all
select 3, 'IBM', NULL, 'P' from dual union all
select 3, 'CTS', NULL, 'P' from dual union all
select 3, 'TCS', NULL, 'P' from dual union all
select 4, 'IBM', NULL, 'OF' from dual union all
select 4, 'CTS', NULL, 'OF' from dual union all
select 4, 'TCS', 'Y', 'ON' from dual union all
select 5, 'CTS', NULL, 'OF' from dual union all
select 5, 'TCS', 'Y', 'ON' from dual
)
select id, source, curr_flag, type
from (select id, source, curr_flag, type,
max(case when source = 'IBM' then 1 end) over (partition by id) ibm,
max(case when source != 'IBM' then 1 end) over (partition by id) not_ibm,
max(case when curr_flag = 'Y' then 1 end) over (partition by id) flag_y
from t)
where ibm = 1
and not_ibm = 1
and flag_y = 1
order by id, source;

Resources