Oracle SQL - Assign queue of customers to list of Employees - sql

I need to distribute list of employees to customers.
For example:
Table 1: List of Employees: A, B & C
Table 2: List of Customers: 1, 2, 3, 4, 5, 6, 7, 8, 9
The needed result:
|------------|------------|
| Customers | Employees |
|------------|------------|
| 1 | A |
| 2 | B |
| 3 | C |
| 4 | A |
| 5 | B |
| 6 | C |
| 7 | A |
| 8 | B |
| 9 | C |
|------------|------------|

You can use ROW_NUMBER() to assign a number on the fly to the employees, and the MOD() to do the rolling join. For example:
select
c.id,
e.name
from (
select t.*,
row_number() over(order by name) as rn
from employees t
) e
join customers c on e.rn =
mod(rn, (select count(*) from customers)) + 1

Number all rows, then use a modulo function for the join:
with e as
(
select employee, row_number() over (order by employee) as rn
from employees
)
, c as
(
select customer, row_number() over (order by customer) as rn
from customers
)
select c.customer, e.employee
from c
join e on e.rn - 1 = mod(c.rn - 1, (select count(*) from e))
order by c.customer;
Demo: https://dbfiddle.uk/?rdbms=oracle_18&fiddle=43a6ca7469dff023d5513fa209e33ea7

You can use MOD function for this purpose. Try below code.
CREATE TABLE EMP
AS
SELECT 'A' AS EMP FROM DUAL
UNION ALL
SELECT 'B' AS EMP FROM DUAL
UNION ALL
SELECT 'C' AS EMP FROM DUAL;
CREATE TABLE CUST
AS
SELECT '1' AS CUST FROM DUAL
UNION ALL
SELECT '2' AS CUST FROM DUAL
UNION ALL
SELECT '3' AS CUST FROM DUAL
UNION ALL
SELECT '4' AS CUST FROM DUAL
UNION ALL
SELECT '5' AS CUST FROM DUAL
UNION ALL
SELECT '6' AS CUST FROM DUAL
UNION ALL
SELECT '7' AS CUST FROM DUAL
UNION ALL
SELECT '8' AS CUST FROM DUAL
UNION ALL
SELECT '9' AS CUST FROM DUAL;
SELECT CUST, EMP
FROM (SELECT ROW_NUMBER () OVER (PARTITION BY 1 ORDER BY EMP) AS ID, EMP
FROM EMP) EMP
INNER JOIN CUST ON MOD (TO_NUMBER (CUST.CUST) - 1, 3) = EMP.ID - 1
ORDER BY 1;

Related

Select rows when a value appears multiple times

I have a table like this one:
+------+------+
| ID | Cust |
+------+------+
| 1 | A |
| 1 | A |
| 1 | B |
| 1 | B |
| 2 | A |
| 2 | A |
| 2 | A |
| 2 | B |
| 3 | A |
| 3 | B |
| 3 | B |
+------+------+
I would like to get the IDs that have at least two times A and two times B. So in my example, the query should return only the ID 1,
Thanks!
In MySQL:
SELECT id
FROM test
GROUP BY id
HAVING GROUP_CONCAT(cust ORDER BY cust SEPARATOR '') LIKE '%aa%bb%'
In Oracle
WITH cte AS ( SELECT id, LISTAGG(cust, '') WITHIN GROUP (ORDER BY cust) custs
FROM test
GROUP BY id )
SELECT id
FROM cte
WHERE custs LIKE '%aa%bb%'
I would just use two levels of aggregation:
select id
from (select id, cust, count(*) as cnt
from t
where cust in ('A', 'B')
group by id, cust
) ic
group by id
having count(*) = 2 and -- both customers are in the result set
min(cnt) >= 2 -- and there are at least two instances
This is one option; lines #1 - 13 represent sample data. Query you might be interested in begins at line #14.
SQL> with test (id, cust) as
2 (select 1, 'a' from dual union all
3 select 1, 'a' from dual union all
4 select 1, 'b' from dual union all
5 select 1, 'b' from dual union all
6 select 2, 'a' from dual union all
7 select 2, 'a' from dual union all
8 select 2, 'a' from dual union all
9 select 2, 'b' from dual union all
10 select 3, 'a' from dual union all
11 select 3, 'b' from dual union all
12 select 3, 'b' from dual
13 )
14 select id
15 from (select
16 id,
17 sum(case when cust = 'a' then 1 else 0 end) suma,
18 sum(case when cust = 'b' then 1 else 0 end) sumb
19 from test
20 group by id
21 )
22 where suma = 2
23 and sumb = 2;
ID
----------
1
SQL>
You can use group by and having for the relevant Cust ('A' , 'B')
And query twice (I chose to use with to avoid multiple selects and to cache it)
with more_than_2 as
(
select Id, Cust, count(*) c
from tab
where Cust in ('A', 'B')
group by Id, Cust
having count(*) >= 2
)
select *
from tab
where exists ( select 1 from more_than_2 where more_than_2.Id = tab.Id and more_than_2.Cust = 'A')
and exists ( select 1 from more_than_2 where more_than_2.Id = tab.Id and more_than_2.Cust = 'B')
What you want is a perfect candidate for match_recognize. Here you go:
select id_ as id from t
match_recognize
(
order by id, cust
measures id as id_
pattern (A {2, } B {2, })
define A as cust = 'A',
B as cust = 'B'
)
Output:
Regards,
Ranagal

SQL query to select e.g. buyer which made 3 specific buys

I have a table like this:
ToyStore
+----+------------+-------------------+
| ID | NAME | PURCHASE |
+----+------------+-------------------+
| 1 | Ramesh | Teddy bear |
| 2 | Khilan | Drum |
| 3 | Chaitali | Chess |
| 4 | Hardik | Wooden sword |
|... | ... | ... |
+----+------------+-------------------+
I need to select all the buyers which have made all 3 purchases - Teddy bear, Chess and Wooden sword and their purchases
As a result there should be something like this:
+--------+-------------------+
| NAME | PURCHASE |
+--------+-------------------+
| Ramesh | Teddy bear |
| Ramesh | Chess |
| Ramesh | Wooden sword |
| Khilan | Teddy bear |
| Khilan | Chess |
| Khilan | Wooden sword |
+--------+-------------------+
Thanks in advance
select * from ToyStore ts1 where
exists (select 1 from ToyStore ts2 where
ts1.name=ts2.name and purchase ='Teddy bear')
and
exists (select 1 from ToyStore ts2 where
ts1.name=ts2.name and purchase ='Chess')
and
exists (select 1 from ToyStore ts2 where
ts1.name=ts2.name and purchase ='Wooden sword')
If you want to find all names with exact one row of each matching toy and only These three rows you could use the following query:
(of course there are smarter ways, but this is very easy to understand and to Change)
With
-- Data
TOYSTORE AS (
SELECT 1 ID, 'Ramish' NAME, 'Teddy Bear' PURCHASE FROM DUAL UNION ALL
SELECT 2, 'Khilan', 'Drum' FROM DUAL UNION ALL
SELECT 3, 'Chaitali', 'Chess' FROM DUAL UNION ALL
SELECT 4, 'Hardik', 'Wooden sword' FROM DUAL UNION ALL
SELECT 5, 'Hardik', 'Chess' FROM DUAL UNION ALL
SELECT 6, 'Hardik', 'Teddy Bear' FROM DUAL UNION ALL
SELECT 7, 'Chaitali', 'Chess' FROM DUAL UNION ALL
SELECT 8, 'Chaitali', 'Wooden sword' FROM DUAL UNION ALL
SELECT 9, 'Chaitali', 'Teddy Bear' FROM DUAL UNION ALL
SELECT 10, 'Khilan', 'Chess' FROM DUAL UNION ALL
SELECT 11, 'Khilan', 'Wooden sword' FROM DUAL UNION ALL
SELECT 12, 'Khilan', 'Teddy Bear' FROM DUAL
),
-- Matching items
MyMatches AS (
SELECT 'Teddy Bear' PURCHASE FROM DUAL UNION ALL
SELECT 'Chess' FROM DUAL UNION ALL
SELECT 'Wooden sword' FROM DUAL
),
-- all single hits of matching items
MyStrikes AS (
SELECT NAME, PURCHASE, COUNT(*) CNT
FROM TOYSTORE
NATURAL JOIN MyMatches
GROUP BY NAME, PURCHASE
HAVING COUNT(*) = 1
),
-- all names with exactly one item of each kind
My3Strikes AS (
SELECT NAME, SUM(CNT) CNT
FROM MyStrikes
GROUP BY NAME
HAVING SUM(CNT) = 3
),
-- number of all not matching items
MyBlanks AS (
SELECT NAME, COUNT(PURCHASE) CNT --COUNT(PURCHASE) CNT
FROM TOYSTORE
WHERE NOT PURCHASE IN ('Teddy Bear', 'Chess', 'Wooden sword')
GROUP BY NAME
),
-- all names
MyNames AS (
SELECT D.NAME
FROM TOYSTORE D
GROUP BY D.NAME
)
-- the result
SELECT D.NAME
FROM MyNames D
JOIN My3Strikes S ON S.Name = D.Name
LEFT JOIN MyBlanks B ON B.Name = D.Name
WHERE B.CNT IS NULL
;
There are some things not completely clear for me. Do you want to know, whether any 3 items are in list? If so, the next query would help.
SELECT NAME, COUNT(PURCHASE)
FROM TOYSTORE
GROUP BY NAME
HAVING COUNT(PURCHASE) = 3
gives all names, that have exactly 3 items in his list.
This query you need for an inner join:
SELECT T.NAME, T.PURCHASE
FROM TOYSTORE T
JOIN (
SELECT NAME, COUNT(PURCHASE)
FROM TOYSTORE
GROUP BY NAME
HAVING COUNT(PURCHASE) = 3
) I
ON I.NAME = T.NAME
Does this solve your Problem?
If not, tell us more.
Questions may be:
What, if someone buys 2 teddybears and 1 sword?
What, if someone buys all three items and another more item?

Oracle - count records if number of token less than 2

I have records like...
ID | KEY
-------|---------
1 | 123_456_abc
1 | 123_xyz
1 | 456_abc
2 | 123_abc
2 | 122_73_zcc
3 | 123_wer
4 | 345_23_fhd
4 | 3453_abc
5 | ad1fr2h3_abcasd
5 | ers2g45bb_abc2rtd
5 | asf23g_abc1_sf45
I want count(ID) where count(tokanize(numeric(KEY),'_')) < 2
As count(ID) will be 6
You can try something like this
SELECT COUNT(ID) FROM xyz WHERE key NOT LIKE '%_%_%';
This should filter all elements which have less than two underscores.
Try this :
select Count(1) from
(with abc(id,key) as (select '1','123_456_abc' from dual
Union all
select '1','123_xyz' from dual
UNion all
select '1','456_abc' from dual
Union all
select '2','123_abc' from dual
UNion all
select '2','123_73_zcc' from dual
Union all
select '3','123_wer' from dual
UNion all
select '1','345_23_fhd' from dual
UNion all
select '1','345_abc' from dual
)
select key, length(regexp_replace(key,'[^_]*','')) cntr
from abc )
where cntr = 1
eliminate all records which has more than 1 underscores
then eliminate the ones which do not start with a number
then sum it up
select sum(cnt) from (
select key, cnt, id from (
select key, length(regexp_replace(key,'[^_]*','')) cnt, id from table_name
) where cnt < 2
) where regexp_like(key,'[1-9]+(.)*')

Select Duplicate records in Oracle

I have a table below in Oracle
Table1
State | Product |other fields
CA | P1 | xxxx
OR | P1 | xxxx
OR | P1 | xxxx
OR | P1 | xxxx
WA | P1 | xxxx
VA | P2 | xxxx
My Output should be only select if State has been occurred more than once.
State | Product |other fields
OR | P1 | xxxx
If you just want to count duplicate states then:
SELECT DISTINCT
State,
Product,
Other_Fields
FROM (
SELECT t.*,
COUNT(1) OVER ( PARTITION BY State ) AS cnt
FROM Table1 t
)
WHERE cnt > 1;
If you want to consider duplicate rows (considering all fields) then:
SELECT *
FROM Table1
GROUP BY State, Product, Other_Fields
HAVING COUNT(1) > 1;
select state, product, column_3, column_4
from (
select state, product, column_3, column_4,
count(*) over (partition by state) as cnt
from the_table
) t
where cnt > 1;
You could use ROW_NUMBER analytic function.
For example,
SQL> WITH sample_data AS(
2 SELECT 'CA' State, 'P1' product FROM dual UNION ALL
3 SELECT 'OR', 'P1' product from dual union all
4 SELECT 'OR', 'P1' product FROM dual UNION ALL
5 SELECT 'OR', 'P1' product FROM dual UNION ALL
6 SELECT 'WA', 'P1' product FROM dual UNION ALL
7 SELECT 'VA', 'P2' product from dual
8 )
9 -- end of sample_data mimicking real table
10 SELECT distinct state,
11 product
12 FROM
13 (SELECT state,
14 product,
15 row_number() OVER(PARTITION BY state ORDER BY product) rn
16 FROM sample_data
17 )
18 WHERE rn >1;
ST PR
-- --
OR P1
SQL>

Use SUM function in oracle

I have a table in Oracle which contains :
id | month | payment | rev
----------------------------
A | 1 | 10 | 0
A | 2 | 20 | 0
A | 2 | 30 | 1
A | 3 | 40 | 0
A | 4 | 50 | 0
A | 4 | 60 | 1
A | 4 | 70 | 2
I want to calculate the payment column (SUM(payment)). For (id=A month=2) and (id=A month=4), I just want to take the greatest value from REV column. So that the sum is (10+30+40+70)=150. How to do it?
You can also use below.
select id,sum(payment) as value
from
(
select id,month,max(payment) from table1
group by id,month
)
group by id
Edit: for checking greatest rev value
select id,sum(payment) as value
from (
select id,month,rev,payment ,row_number() over (partition by id,month order by rev desc) as rno from table1
) where rno=1
group by id
This presupposes you don't have more than one value per rev. If that's not the case, then you probably want a row_number analytic instead of max.
with latest as (
select
id, month, payment, rev,
max (rev) over (partition by id, month) as max_rev
from table1
)
select sum (payment)
from latest
where rev = max_rev
Or there's this, if I've understood the requirement right:
with demo as (
select 'A'as id, 1 as month, 10 as payment, 0 as rev from dual
union all select 'A',2,20,0 from dual
union all select 'A',2,30,1 from dual
union all select 'A',3,40,0 from dual
union all select 'A',4,50,0 from dual
union all select 'A',4,60,1 from dual
union all select 'A',4,70,2 from dual
)
select sum(payment) keep (dense_rank last order by rev)
from demo;
You can check the breakdown by including the key columns:
with demo as (
select 'A'as id, 1 as month, 10 as payment, 0 as rev from dual
union all select 'A',2,20,0 from dual
union all select 'A',2,30,1 from dual
union all select 'A',3,40,0 from dual
union all select 'A',4,50,0 from dual
union all select 'A',4,60,1 from dual
union all select 'A',4,70,2 from dual
)
select id, month, max(rev)
, sum(payment) keep (dense_rank last order by rev)
from demo
group by id, month;
select sum(payment) from tableName where id='A' and month=2 OR month=4 order by payment asc;