Select records based on variable known value - sql

I need to select a series of records based on a known value. The record IDs follow a hierarchical structure where the first 4 characters are constant (IA09), followed by 4 digits representing an organization, followed by 4 characters representing an entity within the parent organization. The goal is to select all "child" records of the known value, as well as the "known value" record.
Sample Data Set:
IA0900000000
IA0912340000
IA0912340109
IA0912340418
IA0912340801
IA0945810000
IA0945810215
IA0945810427
IA0945810454
Here is the same dataset, indented to illustrate the hierarchical structure.
IA0900000000
IA0912340000
IA0912340109
IA0912340418
IA0912340801
IA0945810000
IA0945810215
IA0945810427
IA0945810454
Example 1
If the known value is 'IA0900000000', I need to select all records in the dataset.
Example 2
If the known value is 'IA0945810000', I need to select all records that begin with 'IA094581'
Example 3
If the known value is 'IA0912340109', I need to select ONLY the record with that ID as it has no child records.
The actual dataset is quite larger than this sample, and the known value will be different for each user of the database.
Is there a simple comparison I could employ in the WHERE clause that will give me the correct subset of records?

Assuming your table is called YourTable and the column name is column. You could remove the trailing 0 from your search term and concatenate it with a wildcard (%) and use the LIKE operator like so:
SELECT *
FROM YourTable
WHERE column LIKE TRIM(TRAILING '0' FROM 'IA0945810000') || '%'

You can use simple "like" in your case:
with t(org) as (-- test_data:
select 'IA0900000000' from dual union all
select 'IA0912340000' from dual union all
select 'IA0912340109' from dual union all
select 'IA0912340418' from dual union all
select 'IA0912340801' from dual union all
select 'IA0945810000' from dual union all
select 'IA0945810215' from dual union all
select 'IA0945810427' from dual union all
select 'IA0945810454' from dual
)
select
regexp_replace(
regexp_replace(
regexp_replace(t.org,'0{4}')
,'0{4}')
,'(.{4})'
,'\1.'
) as short_org_path -- just for better readability
,t.*
from t
where t.org like regexp_replace(regexp_replace('&input_org','0{4}'),'0{4}')||'%'
/

Related

SQL get all the columns form a user that has at least 2 vowels in the column name and also its length is superior to 8 in ORACLE DBMS

for this assignment I have to return the names of the columns in all tables from a specific user where the column name has at least 2 vowels and the its length is superior to 8. I know that sql can handle regular expression unfortunately I don't know if it can be used in this case.
Here is a practical example of what I want my request to do.
Here is the existing tables and columns
Table name
Column name
Person
identification
Person
name
Person
last_name
City
continent
City
country
City
name
City
Strength
Here is the desired output from the request
Table name
Column name
Person
identification
Person
last_name
City
continent
I'm using Oracle DMBS.
Thank you for your time
Functions used:
Translate alter characters from text to text identified
Replace substitute value in string from one value to another.
Length counts characters in string.
Notes:
Since you indicated regular expressions may not be able to be used...
we use translate to remove all the vowels setting them to special character "~" Though I suppose we could have just used space ' '.
Translate can't be to an empty string but replace can be.
"Special" character "~" in the translate so if a column name has one of those this can result in an incorrect result to bypass the limit of it can't be blank.
replace to eliminate the speical character.
length's to ensure we removed at least 1 vowel.
Included column "Z" to show you what the translate & replace are doing.
not ideal in terms of performance because of the inability to use indexes owing to the fact we are using functions on data and limiting by that altered data.
Demo Pay attention to the comments for a simpler solution which avoids replace by including ~ in the translate.
WITH CTE AS (SELECT 'Person' "Table name", 'identification' "Column name" from dual union all
SELECT 'Person','name' from dual union all
SELECT 'Person','last_name' from dual union all
SELECT 'City','continent' from dual union all
SELECT 'City','country' from dual union all
SELECT 'City','name' from dual union all
SELECT 'City','Strength' from dual)
SELECT "Table name", "Column name", replace(translate("Column name",'aeiou','~'),'~','') z
FROM CTE
WHERE length("Column name")-length(replace(translate("Column name",'aeiou','~'),'~',''))>=2
and length("Column name")>8
Giving us:
+------------+----------------+---------+
| Table name | Column name | Z |
+------------+----------------+---------+
| Person | identification | dntfctn |
| Person | last_name | lst_nm |
| City | continent | cntnnt |
+------------+----------------+---------+
I'm sure the regex could be a bit more elegant, but something like the following would get you in the ballpark:
SELECT * FROM yourtable WHERE regexp_like(ColumnName, '^.*[aeiou]+.*[aeiou]+.*$', 'i') and LENGTH(ColumnName) > 8
Here's one way: compare the length of the word to the length of the word after the vowels are removed.
with data as (
select 'outLOOK' as word from dual union all
select 'today' as word from dual union all
select 'help' as word from dual
)
SELECT word
FROM data
WHERE LENGTH (word)
- NVL ( LENGTH ( TRANSLATE ( word
, 'xAEIOUaeiou'
, 'x'
)
)
, 0
) >= 2
;
Or
with data as (
select 'outLOOK' as word from dual union all
select 'today' as word from dual union all
select 'help' as word from dual
)
SELECT word
FROM data
WHERE REGEXP_LIKE ( word
, '[AEIOUaeiou].*[AEIOUaeiou]'
);
This searches for
a vowel
any number of characters, 0 or more
another vowel.

Match the end half of an expersion to only letters SQL ORACLE

I've got a column of data in an SQL table and after a first pass it contains only data starting with 'BVH' and 'BVG'. I want it to only contain data that has 3 numeric characters after this and no more letters. I have tried
OUC not like 'BVG%[a-z]%' and OUC not like 'BVH%[a-z]%'
as I don't know if the letters are going to fall in the first, second or third (or multiple) positions following the first 3 letters. I also can't know exactly what letters are going to appear
Example data
BVH122
BVH174
BVH336
BVH123
BVH447
BVH447
BVH321
BVH573
BVG1NS
BVG1T2
BVH283
BVH172
BVG12T
BVG1T2
Is that what you need?
with dat as (select 'BVH122' a from dual
union all select 'BVH174' a from dual
union all select 'BVG1NS' a from dual
union all select 'BVH172' a from dual
union all select 'BVG12T' a from dual
)
select * from dat where regexp_like(a,'[A-Z]{3}[0-9]{3}');
So your query is:
select * from table where regexp_like(OUC,'BV[GH]{1}[0-9]{3}');

DB2: fill a dummy field with values in for loop while a select

I want to fill a dummy field with values in a for loop during a select:
Somethinhg like (table account e.g. has a field "login")
select login,(for i= 1 to 3 {list=list.login.i.","}) as list from account
The result should be
login | list
aaa | aaa1,aaa2,aaa3
bbb | bbb1,bbb2,bbb3
ccc | ccc1,ccc2,ccc3
Can someone please help me if that is possible !!!!
Many Thanks !
If this is an one-off task and the size of your loop is fixed, you can make up a table of integers and do a cartesian product with your table containing the column login:
SELECT ACC.LOGIN || NUMBRS.NUM FROM
ACCOUNT ACC, TABLE (
SELECT '1' AS NUM FROM SYSIBM.SYSDUMMY1 UNION
SELECT '2' AS NUM FROM SYSIBM.SYSDUMMY1 UNION
SELECT '3' AS NUM FROM SYSIBM.SYSDUMMY1
) NUMBRS
which will give you strings like 'aaa1', 'aaa2', 'aaa3' one string per row. Then, you can aggregate these strings with LISTAGG.
If the size is not fixed, you can always make up a temporary table and fill it up with appropriate data and use it instead of the NUMBRS table above.

Comparing values in oracle when one value is partially masked

Here is what I am trying to do in a Oracle SQL query:
I have an account number that is X characters long (Example: 6001055555). I have a table that has part of the same account number but most of the number is masked (Examples: 600##########, 6001######, 600244####).
I am trying to match the number passed in 6001055555 to one of the following values 600##########, 6001######, 600244####.
In this example, account number 6001055555 should return 6001###### (from the above list). I can get to the point where the lengths are the same but am not sure how to address the match - I am looking at using REGEX expressions but am not sure if that' the correct path.
You can use the regular LIKE comparison in this case:
SQL> WITH DATA AS (
2 SELECT '600##########' acct FROM dual UNION ALL
3 SELECT '6001######' acct FROM dual UNION ALL
4 SELECT '600244####' acct FROM dual
5 )
6 SELECT *
7 FROM DATA
8 WHERE '6001055555' LIKE REPLACE (acct, '#', '_');
ACCT
-------------
6001######
We're used to seeing COLUMN LIKE :var but switching terms is also valid (:var LIKE column).
If my understanding is rite, this is what u may be expecting...
select regexp_substr('6001055555',replace('600##########','#'),1) from dual;
If you got any value from this query you may conclude that the account number is matched with the masking values

How to categorize several columns in one statement in SQL/PLSQL

I've got a table with 20 columns which I like to categorize like;
0-25 --> 1
25-50 --> 2
50-75 --> 3
75-100 --> 4
I prefer not to use 20 case ... when statements. Anyone who knows how to do this more dynamically & efficiently? Can be SQL or PL/SQL.
I tried some PL/SQL, but I didn't see a simple method to use the column names as variables.
Many thanks.
Frans
Your example is a bit confusing, but assuming you want to put a certain value into those categories, the function width_bucket might be what you are after:
Something like this:
with sample_data as (
select trunc(dbms_random.value(1,100)) as val
from dual
connect by level < 10
)
select val, width_bucket(val, 0, 100, 4) as category
from sample_data;
This will assign the numbers 1-4 to the (random) values from sample_data. the 0, 100 defines the range from which to build the buckets, and the final parameter 4 says in how many (equally wide) buckets this should be distributed. The result of the function is the bucket into which the value val would fall.
SQLFiddle example: http://sqlfiddle.com/#!4/d41d8/10721
The case statement is probably the most efficient way of doing it. A more dynamic way would be to create a table using the with statement. Here is an example of the code:
with ref as (
select 0 as lower, 25 as higher 1 as val from dual union all
select 25, 59, 2 from dual union all
select 50, 75, 3 from dual union all
select 75, 100, 4 from dual
)
select ref.val
from t left outer join ref
on t.col >= ref.lower and t.col < ref.higher
That said, this particular lookup could be done with arithmetic:
select trunc((t.col - 1) / 25) + 1 as val
from t
And, if your problem is managing the different columns, you might consider unpivot. However, I think it is probably easier just to write the code and modify the column names in a text editor or Excel.