Select to join two tables with replace function/ORACLE SQL - sql

I've got two tables:
Table_promo
Name | Code |
Promo1 | 123 |
Promo2 | 124 |
Promo3 | 125 |
And second table:
Table_invoice
Index | Promo | Price
1155 | 123+ | 1.25
2754 | 125K | 3.26
2378 | 124+ | 2.28
I need select that will give me every index from table_invoice with name of the promo from table_promo. The problem is that in table_invoice there are chars '+' or 'K' at the end of the promo number so I can't simply compare promo codes between two tables.
I've tried writing a select subquery like that:
(select name from table_promo where table_promo.code=to_number(replace(replace(table_invoice.promo,'+',''),'K','')
to replace every '+' and 'K' with empty char ''
It doesn't work, I get error
ORA-01427: single-row subquery returns more than one row
I think that the problem is with converting data in table_invoice.promo and table_promo.code
I've tried converting both to_number, both to_char and using the 'like' clausule between, nothing helps
I am sure that there is another way to delete this chars from table_invoice.promo in this select and compare it to table_promo.code, but can't get any info in the internet

Just use concatenation:
select . . .
from table_promo p join
table_invoice i
on i.promo = p.code || '+';
I think you want the inverse to handle + and K:
select . . .
from table_promo p join
table_invoice i
on p.promo like i.promo || '_'
In the meantime, you should fix the data model. The connection to code should use an exact code. You can store the + and K information in a separate column.

select *
from table_promo p
join table_invoice I
on regexp_substr (I.promo,'^\d+') =
P.code

Related

REGEXP_LIKE DB2 regex pattern

I tried to query search pattern this my sql query but unable to get the result.
SELECT * FROM table1 where REGEXP_LIKE(paramater,'^*RHEL[RHEL77435]','i')
my goal is to search pattern that start with first 4 character including * (*RHEL) and contains any character input in meta-character[ ] and this case is (RHEL77435).
SELECT * FROM table1 where REGEXP_LIKE(paramater,'^*RH[RHEL77435]','i')
when I tried 2 character (RH) it works for me
expected output:
|---------------------|------------------|----------------|
| ruleid | parameter | Operating |
|---------------------|------------------|--------------- |
| 1 | *RHEL | Linux |
|---------------------|------------------|----------------|
Thank you.
Why not just use =?
SELECT *
FROM table1
WHERE parameter = '*RHEL';
Or, if there could be additional characters after '*RHEL', then use LIK:
SELECT *
FROM table1
WHERE parameter LIKE '*RHEL%';

regex to convert alphanumeric and special characters in a string to * in oracle

I have a requirement to convert all the characters in my string to *. My string can also contain special characters as well.
For Example:
abc_d$ should be converted to ******.
Can any body help me with regex like this in oracle.
Thanks
Use REGEXP_REPLACE and replace any single character (.) with *.
SELECT
REGEXP_REPLACE (col, '.', '*')
FROM yourTable
Demo
Instead of regex you could also use
select rpad('*', length('abc_d$ s'),'*') from dual
-- use '*' and pad it until length fits with other *
Doku: rpad(string,length,appendWhat)
Repeat with a string of '*' should work as well: repeat(string,count) (not tested)
regex or rpad makes no difference - they are optimized down to the same execution plan:
n-th try of rpad:
Plan Hash Value : 1388734953
-----------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost | Time |
-----------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 2 | 00:00:01 |
| 1 | FAST DUAL | | 1 | | 2 | 00:00:01 |
-----------------------------------------------------------------
n-th try of regex_replace
Plan Hash Value : 1388734953
-----------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost | Time |
-----------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 2 | 00:00:01 |
| 1 | FAST DUAL | | 1 | | 2 | 00:00:01 |
-----------------------------------------------------------------
So it does not matter wich u use.
THIS IS NOT AN ANSWER
As suggested by Tom Biegeleisen’s brother Tim, I ran a test to compare a solution based on regular expressions to one using just standard string functions. (Specifically, Tim's answer with regular expressions vs. Patrick Artner's solution using just LENGTH and RPAD.)
Details of the test are shown below.
CONCLUSION: On a table with 5 million rows, each consisting of one string of length 30 (in a single column), the regular expression query runs in 21 seconds. The query using LENGTH and RPAD runs in one second. Both solutions read all the data from the table; the only difference is the function used in the SELECT clause. As noted already, both queries have the same execution plan, AND the same estimated cost - because the cost does not take into account differences in function calculation time.
Setup:
create table tbl ( str varchar2(30) );
insert into tbl
select a.str
from ( select dbms_random.string('p', 30) as str
from dual
connect by level <= 100
) a
cross join
( select level
from dual
connect by level <= 50000
) b
;
commit;
Note that there are only 100 distinct values, and each is repeated 50,000 times for a total of 5 million values. We know the values are repeated; Oracle doesn't know that. It will really do "the same thing" 5 million times, it won't just do it 100 times and then simply copy the results; it's not that smart. This is something that would be known only by seeing the actual stored data, it's not known to Oracle beforehand, so it can't "prepare" for such shortcuts.
Queries:
The two queries - note that I didn't want to send 5 million rows to screen, nor did I want to populate another table with the "masked" values (and muddy the waters with the time it takes to INSERT the results into another table); rather, I compute all the new strings and take the MAX. Again, in this test all "new" strings are equal to each other - they are all strings of 30 asterisks - but there is no way for Oracle to know that. It really has to compute all 5 million new strings and take the max over them all.
select max(new_str)
from ( select regexp_replace(str, '.', '*' ) as new_str
from tbl
)
;
select max(new_str)
from ( select rpad('*', length(str), '*') as new_str
from tbl
)
;
Try this:
SELECT
REGEXP_REPLACE('B^%2',
'*([A-Z]|[a-z]|[0-9]|[ ]|([^A-Z]|[^a-z]|[^0-9]|[^ ]))', '*') "REGEXP_REPLACE"
FROM DUAL;
I have included for white spaces too
select name,lpad(regexp_replace(name,name,'*'),length(name),'*')
from customer;

combine 2 sql's from different tables into one query

I have for example as first query: (ararnr = article number)
Select ararnr,ararir,aoarom from ar left join ao ON AR.ARARNR=AO.AOARNR WHERE AR.ARARKD=1389
the second query uses the result from the first column from the first query to search in another table
Select votgan, sum(ststan) as totalStock from vo INNER JOIN st on vo.voarnr=st.starnr where voarnr = ararnr
How could I combine both ?
Please note : Not all articlenumbers from the first query will be found in the second, but I need them in my result.
In the result I need the columns from both queries.
EDIT
for example :
first query returns article numbers and the description:
+---------+--------------+
| ararnr | aoarom |
+---------+--------------+
| a123456 | description1 |
| b123456 | description2 |
| 0123456 | description3 |
+---------+--------------+
second query returns the totalstock for those articles:
+---------+--------------+
| ararnr | totalstock |
+---------+--------------+
| a123456 | 12 |
| b123456 | |
| 0123456 | 6 |
+---------+--------------+
Note the second one doesn't return a value since the articlenumber doesn't exist in this table.
In my result I would like to get the articlenumber with corresponding description and stock.
+---------+--------------+-----------+---------+
| ararnr | aoarom | totalStock| vovoan |
+---------+--------------+-----------+---------+
| a123456 | description1 | 12 | 2 |
| b123456 | description2 | | 1 |
| 0123456 | description3 | 6 | |
+---------+--------------+-----------+---------+
I'm using sql on db2
SECOND EDIT
The first query will select some article numbers (ararnr) from table ar and find the corresponding description (aoarom) in another table ao.
The second query finds the stock (vovoan and sum ststan) from two differend tables vo and st for the article numbers found in the first query.
The result should have the article number with corresponding description with corresponding stock from vo and st
I can't fully understand what you're asking, but another join may assist you.
example:
SELECT ar.ararnr, ar.ararir, ar.ararom, vo.votgan, SUM(vo.ststan) as totalStock
FROM ar LEFT JOIN ao ON [id=id] LEFT JOIN vo ON [id=id]
Because I can't tell what your tables structure are, or what you're really asking for, this is the best response I can give you.
This also may be what you're looking for:
Combining 2 SQL queries and getting result set in one
You can use this query.
SELECT ar.ararnr, ar.ararir, ar.ararom, vo.votgan, SUM(vo.ststan) as totalStock
FROM ar
LEFT JOIN ao ON ao.ararnr = ar.ararnr
LEFT JOIN vo ON vo.voarnr = ao.ararnr
If you are using SQL Server as database then this can be done with help of OUTER APPLY
SELECT ararnr,aoarom ,temp.totalStock
FROM ar
LEFT JOIN ao ON AR.ARARNR=AO.AOARNR
OUTER APPLY(
SELECT sum(ststan) as totalStock
FROM vo
INNER JOIN st on vo.voarnr=st.starnr
where voarnr = ar.ararnr
)temp
WHERE AR.ARARKD=1389
You'd get a much more complete answer if you were to post the table structure and desired result, but..
You can use the first query as a resultset for your second query, and join to it. something like:
Select
votgan,
sum(ststan) as totalStock
from vo
inner join (Select
ararnr,
ararir,
ararom
from ar
left join ao .....) z on vo.voarnr = z.ararnr
EDIT:
Select
votgan,
sum(ststan) as totalStock,
z.ararnr,
z.aoarom
from vo
inner join (Select
ararnr,
ararir,
ararom
from ar
left join ao .....) z on vo.voarnr = z.ararnr

Union with one different column

I would like to use UNION to connect two views (view CSP contains 1 more column so I want to use * for 2nd in case of some items from 2nd view are not in 1st view) and that's working good but I have no duplicated configuration id either with right value and with *.
How to solve that and remove lines with '*' when there is value in csp?
SELECT csp.customer_no,
csp.contract,
csp.customer_part_no,
csp.configuration_id,
csp.catalog_no
FROM customersomething csp
UNION
SELECT spc.customer_no,
spc.contract,
spc.customer_part_no,
'*' AS "configuration_id",
spc.catalog_no
FROM
superproduct spc
+-------------+----------+-----+------------------+--------+
| customer_no | contract | ... | configuration_id | |
+-------------+----------+-----+------------------+--------+
| 17 | whatever | ... | * | view A |
| 17 | whatever | ... | right_one | view B |
+-------------+----------+-----+------------------+--------+
First, use union all unless you want to incur the overhead of removing duplicates.
Second, filter out the second one. Here is a way using not exists:
SELECT csp.customer_no, csp.contract, csp.customer_part_no,
csp.configuration_id, csp.catalog_no
FROM customersomething csp
UNION ALL
SELECT spc.customer_no, spc.contract, spc.customer_part_no,
'*' AS "configuration_id", spc.catalog_no
FROM superproduct spc
WHERE NOT EXISTS (SELECT 1
FROM customersomething csp
WHERE scp.customer_no = spc.customer_no
);
You may use this query,
SELECT spc.customer_no,
spc.contract,
spc.customer_part_no,
csp.configuration_id,
spc.catalog_no FROM superproduct spc
LEFT JOIN customersomething csp ON spc.customer_no = csp.customer_no
UNION ALL
SELECT csp.customer_no,
csp.contract,
csp.customer_part_no,
csp.configuration_id,
csp.catalog_no
FROM customersomething csp
LEFT JOIN superproduct spc ON spc.customer_no = csp.customer_no AND spc.customer_no IS NULL

Query to Calculate totalcost based on description

I have question regarding sql script. I have a custom view, below is the data
================================================================================
ql_siteid | ql_rfqnum | ql_vendor | ql_itemnum | totalcost_option | description
================================================================================
SGCT | 1002 | VND001 | ITEM002 | 12500 |
SGCT | 1002 | VND001 | ITEM001 | 1350 |
SGCT | 1002 | VND002 | ITEM002 | 11700 |
SGCT | 1002 | VND002 | ITEM001 | 1470 | Nikon
SGCT | 1002 | VND002 | ITEM001 | 1370 | Asus
================================================================================
And i want the result like below table:
VND001 = 13850
VND002 = Asus 13070, Nikon 13170
where 13850 is come from 12500+1350, 13070 is come from 11700+1370 and 13170 is come from 11700+1470. All the cost is calculated from totalcost_option and will be group based on vendor
So please give me some advise
To get the exact output you required use the following statement: (where test_table is your table name):
SELECT ql_vendor || ' = ' ||
LISTAGG( LTRIM(description||' ')||totalcost, ', ')
WITHIN GROUP (ORDER BY description)
FROM (
WITH base_cost AS (
SELECT ql_vendor, SUM(totalcost_option) sumcost
FROM test_table WHERE description IS NULL
GROUP BY ql_vendor
),
individual_cost AS (
SELECT ql_vendor, totalcost_option icost, description
FROM test_table WHERE description IS NOT NULL
)
SELECT ql_vendor, sumcost + NVL(icost,0) totalcost, description
FROM base_cost LEFT OUTER JOIN individual_cost USING (ql_vendor)
)
GROUP BY ql_vendor;
Details:
The Outer select just takes the individual rows and combines them to the String-representation. Just remove it and you will get a single row for each vendor/description combination.
The inner select joins two sub-select. The first one gets the base_cost for each vendor by summing up all rows without a description. The second gets the individual cost for each row with a description.
The join combines them - and left outer joins displays the base_cost for vendors which don't have a matching row with description.
Assuming you have a version of Oracle 11g or later, using ListAgg will do the combination of the comma separated tuples for you. The rest of the string is generated by simply concatenating the components together from an intermediate table - I've used a derived table (X) here, but you could also use a CTE.
Edit
As pointed out in the comments, there's a whole bunch more logic missing around the Null description items I missed in my original answer.
The following rather messy query does project the required result, but I believe this may be indicative that a table design rethink is necessary. The FULL OUTER JOIN should ensure that rows are returned even if there are no base / descriptionless cost items for the vendor.
WITH NullDescriptions AS
(
SELECT "ql_vendor", SUM("totalcost_option") AS "totalcost_option"
FROM MyTable
WHERE "description" IS NULL
GROUP BY "ql_vendor"
),
NonNulls AS
(
SELECT COALESCE(nd."ql_vendor", mt."ql_vendor") AS "ql_vendor",
NVL(mt."description", '') || ' '
|| CAST(NVL(mt."totalcost_option", 0)
+ nd."totalcost_option" AS VARCHAR2(30)) AS Combined
FROM NullDescriptions nd
FULL OUTER JOIN MyTable mt
ON mt."ql_vendor" = nd."ql_vendor"
AND mt."description" IS NOT NULL
)
SELECT x."ql_vendor" || ' = ' || ListAgg(x.Combined, ', ')
WITHIN GROUP (ORDER BY x.Combined)
FROM NonNulls x
WHERE x.Combined <> ' '
GROUP BY x."ql_vendor";
Updated SqlFiddle here
Your logic seems to be: If description is always NULL for a vendor then you want that as the total cost. Otherwise, you want the NULL value of description added to all the other values. The following query implements this logic. The output is in a different format from your answer -- this format is more consistent with a SQL result set:
select ql_vendor,
(sum(totalcost_option) +
(case when description is not null then max(totalcost_null) else 0 end)
)
from (select v.*, max(description) over (partition by ql_vendor) as maxdescription,
sum(case when description is null then totalcost_option else 0 end) over (partition by ql_vendor) as totalcost_null
from view v
) t
where maxdescription is null or description is not null
group by ql_vendor, description;