Search data from table column using regex in oracle sql - oracle

I need to seach data from table with person`s phone number. But phone number is saved in different forms. But its length equals to 9 and only consist of numbers. How can I find number when I search with static form like 998732387 then result should be.
2 | 99 873 23 87 | Kike
When I enter 971234573 then result should look like below:
3 | 97 123-45-73 | Cris
mytable
-----------------------------------------
id | phone | name
----------------------------------------
1 | 991234567 | Michael
2 | 99 873 23 87 | Kike
3 | 97 123-45-73 | Cris
Please Help me. Any help is appreciated.

One way is to remove all non-digits:
select *
from mytable
where regexp_replace(phone, '[^[:digit:]]', '') = '971234573';

Or, if your database doesn't support regular expressions (hard to believe), translate does the job:
SQL> with test (id, phone, name) as
2 -- your sample data
3 (select 1, '991234567', 'Michael' from dual union all
4 select 2, '99 873 23 87', 'Kike' from dual union all
5 select 3, '97 123-45-73', 'Cris' from dual
6 ),
7 only_digits as
8 -- remove non-digits from the PHONE colunmn (pre-regex version)
9 (select id, phone, name,
10 translate(phone, 'a' || translate(phone, 'x0123456789x', 'x'), 'a') digit
11 from test
12 )
13 select id, phone, name
14 from only_digits
15 where digit = '998732387';
ID PHONE NAME
---------- ------------ -------
2 99 873 23 87 Kike
SQL>

Related

SQL help to count number of locations for each item/branch

I'm a SQL rookie, and am having trouble wrapping my head around how to do the following. I have a table that contains item information by branch. Within a branch an item can be in multiple locations. The data I need to extract needs to include a column that provides the total number of locations (count) the item is associated with for a given branch.
Output would look something like this:
I'm guessing this is a sub query, but to be honest I'm not sure how to get started... order in which this is done (subquery group by first, then join, etc)
In purely logical terms:
SELECT
a.Branch,
a.Item,
a.Loc,
COUNT(a.Branch||a.Item) AS 'LocCount'
FROM BranchInventoryFile a
GROUP BY a.Branch,a.Item
You can tackle this by using Oracle's Count Analytical functions found here. Be sure to read up on WINDOW/Partitioning functions as this unlocks quite a bit of functionality in SQL.
SQL:
SELECT
a.BRANCH,
a.ITEM,
a.LOC,
COUNT(a.ITEM) OVER (PARTITION BY a.BRANCH, a.ITEM) AS LOC_COUNT
FROM
BRANCH a;
Result:
| BRANCH | ITEM | LOC | LOC_COUNT |
|--------|------|------|-----------|
| 100 | A | 1111 | 2 |
| 100 | A | 1112 | 2 |
| 200 | A | 2111 | 1 |
| 200 | B | 1212 | 2 |
| 200 | B | 1212 | 2 |
| 300 | A | 1222 | 1 |
SQL Fiddle:
Here
total number of locations (count) the item is associated with for a given branch
The way you described it, you should
remove location from query:
SQL> with branchinventoryfile (branch, item, location) as
2 (select 100, 'A', 1111 from dual union all
3 select 100, 'A', 1112 from dual union all
4 select 200, 'A', 2111 from dual
5 )
6 select branch,
7 item,
8 count(distinct location) cnt
9 from BranchInventoryFile
10 group by branch, item;
BRANCH I CNT
---------- - ----------
100 A 2
200 A 1
SQL>
if you leave location in select, you have to group by it (and get wrong result):
6 select branch,
7 item,
8 location,
9 count(distinct location) cnt
10 from BranchInventoryFile
11 group by branch, item, location;
BRANCH I LOCATION CNT
---------- - ---------- ----------
100 A 1111 1
200 A 2111 1
100 A 1112 1
SQL>
or include locations, but aggregate them, e.g.
6 select branch,
7 item,
8 listagg(location, ', ') within group (order by null) loc,
9 count(distinct location) cnt
10 from BranchInventoryFile
11 group by branch, item;
BRANCH I LOC CNT
---------- - -------------------- ----------
100 A 1111, 1112 2
200 A 2111 1
SQL>

how to loop through each row of every group (while doing "group by") in Oracle table

I have a table like this:
I want to group by the table base on "customer_id" column and calculate "Day-day[0]" column. "Day-day[0]" is "Day" field in every group and "day[0]" is first row of the day in the group. At the same time, I have to calculate total risk which is in following:
This is the table after grouping by:
This is total risk formula:
In fact, I have to loop through each row of every group to calculate total risk.
My sample table is like this:
CREATE TABLE risk_test
(id VARCHAR2 (32) NOT NULL PRIMARY KEY,
customer_id varchar2 (40BYTE),
risk number,
day VARCHAR2(50 BYTE))
insert into risk_test values(1,102,15,1);
insert into risk_test values(2,102,16,1);
insert into risk_test values(3,104,11,1);
insert into risk_test values(4,102,17,2);
insert into risk_test values(5,102,10,2);
insert into risk_test values(6,102,13,3);
insert into risk_test values(7,104,14,2);
insert into risk_test values(8,104,13,2);
insert into risk_test values(9,104,17,1);
insert into risk_test values(10,104,16,2);
The sample answer is like this:
Would you please guide me how I can do this scenario in Oracle database?
Any help is really appreciated.
Using the sample data that was provided, I believe this query should calculate the risks properly:
Query
SELECT o.*,
ROUND (
SUM (day_minus_day0 * risk) OVER (PARTITION BY customer_id)
/ SUM (day_minus_day0) OVER (PARTITION BY customer_id),
5) AS total_risk
FROM (SELECT rt.*, (rt.day - MIN (rt.day) OVER (PARTITION BY customer_id)) + 1 AS day_minus_day0
FROM risk_test rt) o
ORDER BY customer_id, TO_NUMBER (day), TO_NUMBER (id);
Result
ID CUSTOMER_ID RISK DAY DAY_MINUS_DAY0 TOTAL_RISK
_____ ______________ _______ ______ _________________ _____________
1 102 15 1 1 13.77778
2 102 16 1 1 13.77778
4 102 17 2 2 13.77778
5 102 10 2 2 13.77778
6 102 13 3 3 13.77778
3 104 11 1 1 14.25
9 104 17 1 1 14.25
7 104 14 2 2 14.25
8 104 13 2 2 14.25
10 104 16 2 2 14.25
Your total risk calculation just looks like a weighted average to me. That is, the average risk of the rows for each customer, weighted according to the day offset (day-day[0]), so that risks in later days count for more.
To compute that, you need a common table expression to 1st compute the day-weighted risk for each row. Then you can just compute the weighted average by dividing.
The query below illustrates the approach, with comments.
-- This first WITH clause is just sample data. In your database you would
-- get rid of this and replace all references to "input" with your actual
-- table name
with input ( customer_id, risk, day ) AS (
SELECT 1053, 100, 1 FROM DUAL UNION ALL
SELECT 1053, 100, 1 FROM DUAL UNION ALL
SELECT 1053, 100, 2 FROM DUAL UNION ALL
SELECT 1053, 100, 2 FROM DUAL UNION ALL
SELECT 1053, 100, 3 FROM DUAL UNION ALL
SELECT 1054, 200, 1 FROM DUAL UNION ALL
SELECT 1054, 200, 1 FROM DUAL UNION ALL
SELECT 1054, 200, 3 FROM DUAL UNION ALL
SELECT 1054, 200, 3 FROM DUAL UNION ALL
SELECT 1054, 200, 4 FROM DUAL
),
-- This CTE computes the day offset for each row and multiplies by the risk to
-- compute a day-weighted risk.
-- I added +1 to the day_offset, otherwise risks on the 1st day would not contribute
-- to the total risk, which I think is not what you intended(?)
weighted_input AS (
SELECT i.customer_id,
i.risk,
i.day,
i.day - min(i.day) over ( partition by i.customer_id ) + 1 day_offset,
( i.day - min(i.day) over ( partition by i.customer_id ) + 1 ) * i.risk day_weighted_risk
FROM input i )
-- This is the main SELECT clause that gets all the weighted risks and computes
-- the group total risk, which appears the same in every row in each group.
SELECT wi.*,
sum(wi.day_weighted_risk) over ( partition by wi.customer_id ) / sum(wi.day_offset) over ( partition by wi.customer_id ) total_risk
FROM weighted_input wi;
+-------------+------+-----+------------+-------------------+------------+
| CUSTOMER_ID | RISK | DAY | DAY_OFFSET | DAY_WEIGHTED_RISK | TOTAL_RISK |
+-------------+------+-----+------------+-------------------+------------+
| 1053 | 100 | 1 | 1 | 100 | 100 |
| 1053 | 100 | 1 | 1 | 100 | 100 |
| 1053 | 100 | 2 | 2 | 200 | 100 |
| 1053 | 100 | 2 | 2 | 200 | 100 |
| 1053 | 100 | 3 | 3 | 300 | 100 |
| 1054 | 200 | 1 | 1 | 200 | 200 |
| 1054 | 200 | 1 | 1 | 200 | 200 |
| 1054 | 200 | 3 | 3 | 600 | 200 |
| 1054 | 200 | 3 | 3 | 600 | 200 |
| 1054 | 200 | 4 | 4 | 800 | 200 |
+-------------+------+-----+------------+-------------------+------------+
For your database, having the actual table and not needing the input CTE, it would be:
WITH weighted_input AS (
-- This CTE computes the day offset for each row and multiplies by the risk to
-- compute a day-weighted risk.
-- I added +1 to the day_offset, otherwise risks on the 1st day would not contribute
-- to the total risk, which I think is not what you intended(?)
SELECT i.customer_id,
i.risk,
i.day,
i.day - min(i.day) over ( partition by i.customer_id ) + 1 day_offset,
( i.day - min(i.day) over ( partition by i.customer_id ) + 1 ) * i.risk day_weighted_risk
FROM my_table i )
-- This is the main SELECT clause that gets all the weighted risks and computes
-- the group total risk, which appears the same in every row in each group.
SELECT wi.*,
sum(wi.day_weighted_risk) over ( partition by wi.customer_id ) / sum(wi.day_offset) over ( partition by wi.customer_id ) total_risk
FROM weighted_input wi;

Oracle- count the number rows based on a condition

I want to create a 'ticket' which counts the number of passes for each ID. When we have a gold pass on any of the ID, this means the pass is applied to all those in booking. So for this example, we want to count 5. For the other pass_codes, we want to simply count the number of passes and exclude those that are nulls. I have an expected output below.
Say I have this data:
Passes
ID | GuestID | Pass_code
----------------------------
100 | 001 | Bronze
100 | 002 | Bronze
101 | 103 | Gold
101 | 104 | NULL
101 | 105 | NULL
101 | 106 | NULL
101 | 107 | NULL
102 | 208 | Silver
103 | 209 | Steel
103 | 210 | Steel
103 | 211 | NULL
Passengers
ID | Passengers
-----------------
100 | 2
101 | 5
102 | 1
103 | 3
I want to count then create a ticket in the output of:
ID 100 | 2 pass (bronze)
ID 101 | 5 pass (because it is gold, we count all passengers)
ID 102 | 1 pass (silver)
ID 103 | 2 pass (steel) (2 passes rather than than 3 as we just want to count only the passes for steel, bronze silver)
I want to do something like this, but as a combined query.
DECLARE #ID = 101; -- i will want to pass in IDs
-- for gold, we want to count all passengers when the gold pass is on
SELECT pp.Passengers
FROM passes
JOIN Passengers pp ON p.ID = pp.ID
WHERE p.pass_code IN'%gold%'
AND PP.id = #id
-- for bronze, silver and steel
SELECT
count(p.ID)
FROM Passes
WHERE p.ID = #id
AND P.pass_code IN ('Bronze', 'silver', 'steel') -- Dont want to check based on NUlls as this may chnage to something else.
)
Any help or advice would be much appreciated.
Does this work for you?
with Passes as (
select 100 as id, 001 as guestid, 'Bronze' as passcode from dual union all
select 100 as id, 002 as guestid, 'Bronze' as passcode from dual union all
select 101 as id, 103 as guestid,'Gold' as passcode from dual union all
select 101 as id, 104 as guestid, NULL as passcode from dual union all
select 101 as id, 105 as guestid, NULL as passcode from dual union all
select 101 as id, 106 as guestid, NULL as passcode from dual union all
select 101 as id, 107 as guestid, NULL as passcode from dual union all
select 102 as id, 208 as guestid, 'Silver' as passcode from dual union all
select 103 as id, 209 as guestid, 'Steel' as passcode from dual union all
select 103 as id, 210 as guestid, 'Steel' as passcode from dual union all
select 103 as id, 211 as guestid, NULL as passcode from dual
)
SELECT
id,passcode,count(ID)
FROM Passes
where passcode is not null and passcode<>'Gold'
group by id,passcode
union all
SELECT
id,'Gold',count(ID)
FROM Passes
where id in
(
select id from Passes where passcode='Gold'
)
group by id
order by id
result:
100 Bronze 2
101 Gold 5
102 Silver 1
103 Steel 2
If I understand your question right the query should be like the following
** The table
create table test (ID number, GuestId number, Pass_code varchar2(10));
insert into test values(100,001,'Bronze');
insert into test values(100,002,'Bronze');
insert into test values(101,103,'Gold');
insert into test values(101,104,NULL);
insert into test values(101,105,NULL);
insert into test values(101,106,NULL);
insert into test values(101,107,NULL);
insert into test values(102,208,'Silver');
insert into test values(103,209,'Steel');
insert into test values(103,210,'Steel');
insert into test values(103,211,NULL);
commit;
SQL> select * from test order by 1,2;
ID GUESTID PASS_CODE
---------- ---------- ------------------------------
100 1 Bronze
100 2 Bronze
101 103 Gold
101 104
101 105
101 106
101 107
102 208 Silver
103 209 Steel
103 210 Steel
103 211
11 rows selected.
** The query
WITH PASSES AS (
SELECT T1.ID,T1.PASS_CODE, COUNT(T2.ID) QUANTITY FROM TEST T1, TEST T2
WHERE T1.PASS_CODE='Gold' AND T1.ID=T2.ID
GROUP BY T1.ID,T1.PASS_CODE
UNION ALL
SELECT ID,PASS_CODE, COUNT(*) QUANTITY FROM TEST
WHERE PASS_CODE IS NOT NULL AND
PASS_CODE != 'Gold'
GROUP BY ID,PASS_CODE)
SELECT ID, QUANTITY || ' (' || PASS_CODE || ')' RESULT FROM PASSES
ORDER BY ID;
** Result
ID RESULT
---------- --------------------
100 2 (Bronze)
101 5 (Gold)
102 1 (Silver)
103 2 (Steel)

use LAG with expression in oracle

I have a column (status) in a table that contain numbers and values are 1, 2 or 4.
I would like, in a SQL query, add a calculated column (bitStatus) that will store the bitwise oerator OR for the status column of the current line and the column in the previous line.
like so :
| id | status| bitStatus|
|----|-------|----------|
| 1 | 1 | 1 |
| 2 | 2 | 3 |
| 3 | 4 | 7 |
| 4 | 1 | 7 |
So what I did is to use LAG function in oracle but I coudn't figure out how to do it as long as I want to create only on calculated column bitStatus
my query is like :
select id, status,
BITOR(LAG(bitStatus) OVER (ORDER BY 1), status)) AS bitStatus
But as you know, I can't use LAG(bitStatus) when calculating bitStatus.
So how could I make it the desired table.
Thanks in advance.
Would this help?
lines #1 - 6 represent sample data
the TEMP CTE is here to fetch LAG status value (to improve readability)
the final select does the BITOR operation as bitor(a, b) = a - bitand(a, b) + b
SQL> with test (id, status) as
2 (select 1, 1 from dual union all
3 select 2, 2 from dual union all
4 select 3, 1 from dual union all
5 select 4, 4 from dual
6 ),
7 temp as
8 (select id, status,
9 lag(status) over (order by id) lag_status
10 from test
11 )
12 select id,
13 status,
14 status - bitand(status, nvl(lag_status, status)) + nvl(lag_status, status) as bitstatus
15 from temp
16 order by id;
ID STATUS BITSTATUS
---------- ---------- ----------
1 1 1
2 2 3
3 1 3
4 4 5
SQL>

ORACLE HOW TO GET DATA IN THIS FORMAT

I have a table structure like this in Oracle
Product_ID Product_SKU Product_Zone Product_Price
1 123 Zone1 10
2 123 Zone2 12
3 456 Zone1 13
4 456 Zone2 14
How I can get the result in horizontal format like this
Product_SKU Zone1_Price Zone2_Price
123 10 12
456 13 14
Thanks
Here's a simple, aggregation option:
SQL> with test (product_id, product_sku, product_zone, product_price) as
2 -- sample data
3 (select 1, 123, 'zone1', 10 from dual union all
4 select 2, 123, 'zone2', 12 from dual union all
5 select 3, 456, 'zone1', 13 from dual union all
6 select 4, 456, 'zone2', 14 from dual
7 )
8 select product_sku,
9 max(case when product_zone = 'zone1' then product_price end) zone1_price,
10 max(case when product_zone = 'zone2' then product_price end) zone2_price
11 from test
12 group by product_sku;
PRODUCT_SKU ZONE1_PRICE ZONE2_PRICE
----------- ----------- -----------
123 10 12
456 13 14
SQL>

Resources