How to increase non-numeric column value? - sql

I have a column that is used for a sequence number.
The values in the column look like 'WT0000004568'
I need to find the maximum value and increment the counter part by 1 to create a new sequence number.
For this example, the resultant value would be 'WT0000004569'.
How do I do that?

As Thilio pointed out, this is generally a bad design both from a performance and from a concurrency issue. If we assume that you have a single user system and aren't particularly concerned about performance, you could do something like
SQL> ed
Wrote file afiedt.buf
1 select 'WT' ||
2 to_char(
3 to_number(substr('WT0000004568',3)) + 1,
4 'fm0000000000')
5* from dual
SQL> /
'WT'||TO_CHAR
-------------
WT0000004569

Related

how can function SUBSTR can help me remove vlues from my column

25779101724|GTG1105-Kibimba .These are the telefone numbers, there is more like this.After the numbers the following characters are a location of the telefone number. so i want to remove everything after these numbers(the bar,the location, and the space) so that i can query in the db the numbers which are active among these. and then i dont want to lose these location after numbers because i will need them to report the active numbers AND their location. how can i remove these location and query the active numbers and then replace the location.
I am hoping a response.
From my point of view, your by far the best option is to normalize that table and store each piece of information into its own column. Because, as long as you can easily split that string into several parts, joining it to another table will suffer as number of rows gets higher.
Anyway, here you are.
Sample data:
SQL> with test (msisdn) as
2 (select '25779101724|GTG1105-Kibimba' from dual union all
3 select '25776030896|BRR1351-Kaberenge2' from dual
4 )
Query begins here:
5 select
6 substr(msisdn, 1, instr(msisdn, '|') - 1) phone_number,
7 substr(msisdn, instr(msisdn, '|') + 1) the_rest
8 from test;
PHONE_NUMBER THE_REST
------------------------------ ------------------------------
25779101724 GTG1105-Kibimba
25776030896 BRR1351-Kaberenge2
SQL>

select row based on what a substring in a column might contain

I'm looking to select the primary key of a row and I've only got a column that contains info (in a substring) that I need to select the row.
E.g. MyTable
ID | Label
------------
11 | 1593:#:#:RE: test
12 | 1239#:#:#some more random text
13 | 12415#:#:#some more random text about the weather
14 | 369#:#:#some more random text about the StackOverflow
The label column has always a delimiter of :#:#:
So really I guess, I'd need to be able to split this row by the delimiter, grab the first part of the label column (i.e. the number I'm looking) to get the id I wanted.
So, If I wanted row with ID of 14, then I'd be:
Select ID from MyTable
where *something* = '369'
Any ideas on how to construct something ..or how best to go about this:)
I'm completely stumped and haven't been able to find how to do this.
Thanks,
How about:
WHERE label LIKE '369#%'?
No reason to get fancy.
Although.. if you are going to do this search often, then maybe pre-split that value out to another column as part of your ETL process and index it.

Generate random data in Oracle based on ranks

It is given the following scenario. I have a list of 3000 first names and a list of 2500 last names. Each one of them has a "ranking" that represents the position in a name's top. Two or more names can have the same ranking. Also, a table with 1500 cities is given, each with 4 census values in certain years.
From the tables above I must generate 5 million random entries containing the first name, last name, birth date and place of birth of one person, that should follow the rules given by ranking of the names and population number of the cities.
This have to be generated using just Oracle (stored functions, stored procedures and so on). How can I do this?
Disclaimer: I'm not a statistics expert, and there are probably way more efficient means to do that.
The most challenging task seems to be the creation of 5 million names according to ranks. In real world, those would be distributed unevenly among the population: difference between second last and last would be 1-2 persons, and the difference between the first and second rank could be thousands of people. That said, I have no idea how to achieve that, so we'll model it in other way. Suppose we have total population of 100 and list of four ranked names:
Alice: 1
Bob: 2
Betty: 2
Claire: 3
We can make the distribution "even", so that rank 3 has X people, rank 2 has twice as many, and rank 1 thrice as many. If the ranks were unique, the formula would be as simple as X + 2X + 3X = 100, but we have two names in rank 2, so it should be X + 2*2X + 3X = 100, so X = 12.5. We can truncate it to integer and get people counts for all ranks except the first (12, 24 and 24) and first rank would get what remains: 40. Seems good enough, though it will not work for edge case when you have multiple first ranks.
There's a little problem, though. For 3000 different names, the sum of coefficients would be 4501500. So, truncated X would be 1, making rank 3000 to rank 2 have 1 to 2999 people respectively, and rank 1 have a little under 500000. That's not quite good enough. To illustrate with four names above, assume total count of 15. With current algorithm, X will be 1 as well, and distribution will be 1-2-2-10. Luckily, we'll be processing ranks one by one in procedure, so we can remove processed people from equation and recalculate X. E.G. first it's X + 2*2X + 3X = 15 with X=1, then 2*2X + 3X = 14 with X=2. This way, distribution will be 1-4-4-6, which is far from ideal, but better.
Now, this can already be expressed as PL/SQL. I suggest to create the table with following columns: LAST_NAME, FIRST_NAME, BIRTHDAY, CITY, RAND_ROWNO.
First of all, let's fill it with 5M last names. Assuming your table for them is last_names(name, name_rank), you'll need the following:
declare
cursor cur_last_name_ranks is
select name_rank, count(*) cnt, row_number() over (order by name_rank desc) coeff
from last_names l
group by name_rank;
cursor cur_last_names (c_rank number) is
select name from last_names
where name_rank = c_rank;
v_coeff_sum number;
v_total_people_count number:= 5000000;
v_remaining_people number;
v_x number;
v_insert_cnt number;
begin
--Get a sum of all coefficients for our formula
select sum(coeff) into v_coeff_sum
from
(
select count(*) * row_number() over (order by name_rank desc) coeff
from last_names l
group by name_rank
);
v_remaining_people := v_total_people_count;
--Now, loop for all coefficients
for r in cur_last_name_ranks loop
--Recalculate X
v_x := trunc(v_remaining_people / v_coeff_sum);
--First, determine how many rows should be inserted per last name with such rank
if r.name_rank = 1 then
if r.cnt > 1 then
--raise an exception here, we don't allow multiple first ranks
raise TOO_MANY_ROWS;
end if;
v_insert_cnt := v_remaining_people;
else
v_insert_cnt := v_x*r.coeff;
end if;
--Insert last names N times.
--Instead of multiple INSERT statements, use select from dual with connect trick.
for n in cur_last_names(r.name_rank) loop
insert into result_table(last_name)
select n.name from dual connect by level <= v_insert_cnt;
end loop;
commit;
--Calculate remaining people count
v_remaining_people := v_remaining_people - v_x*r.cnt*r.coeff;
--Recalculate remmaining coefficients
v_coeff_sum := v_coeff_sum - r.cnt*r.coeff;
end loop;
end;
Now you have 5 million rows with last names filled according to ranks. Now, we'll need to assign random number from 1 to 5000000 for each row - you'll see why. This is done with a single query using merge on self:
merge into result_table t1
using (select rowid rid, row_number() over (ORDER BY DBMS_RANDOM.VALUE) rnk from result_table) t2
on (t1.rowid = t2.rid)
when matched then update set t1.rand_rowno = t2.rnk
Note that it will take some time because of large size.
Now you must repeat the same procedure for first names. It'll be very similar to last names, except you'll be updating existing records, not inserting new. If you keep track of how many rows you've updated already, it'll be as simple putting this in the inner loop:
update result_table
set first_name = n.name
where rand_rowno between
(v_processed_rows+1) and
(v_processed_rows+v_insert_cnt);
v_processed_rows := v_processed_rows+v_insert_cnt;
That does it - you now have a decent sample of 5M names according to your ranking, last names randomly matched with first names.
Now, for census. I don't really understand your format, but that's relatively simple. If you get data to the form of "N people were born in city C between DATE1 and DATE2", you can update the table in a loop, setting N rows to have CITY = C and BIRTHDAY = a random date between DATE1 and DATE2. You'll need a function to return a random date from a time period, see this. Also, don't forget to assign random row numbers again before doing that.
I'll leave the census part for you to implement, I've spent too much time on writing this already. Thanks for a good brain exercise!

How to change / convert values in Output that comes from SQL Server table

I have created a view in my SQL Server database which will give me number of columns.
One of the column heading is Priority and the values in this column are Low, Medium, High and Immediate.
When I execute this view, the result is returned perfectly like below. I want to change or assign values for these priorities. For example: instead of Low I should get 4, instead of Medium I should get 3, for High it should be 2 and for Immediate it should be 1.
What should I do to achieve this?
Ticket# Priority
123 Low
1254 Low
5478 Medium
4585 High
etc., etc.,
Use CASE:
Instead of Low I should get 4, instead of Medium I should get 3, for
High it should be 2 and for Immediate it should be 1
SELECT
[Ticket#],
[Priority] = CASE Priority
WHEN 'Low' THEN 4
WHEN 'Medium' THEN 3
WHEN 'High' THEN 2
WHEN 'Immediate' THEN 1
ELSE NULL
END
FROM table_name;
EDIT:
If you use dictionary table like in George Botros Solution you need to remember about:
1) Maintaining and storing dictionary table
2) Adding UNIUQE index to Priority.Name to avoid duplicates like:
Priority table
--------------------
Id | Name | Value
--------------------
1 | Low | 4
2 | Low | 4
...
3) Instead of INNER JOIN defensively you ought to use LEFT JOIN to get all results even if there is no corresponding value in dictionary table.
I have an alternative solution for your problem by creating a new Priority table (Id, Name, Value)
by joining to this table you will be able to select the value column
SELECT Ticket.*, Priority.Value
FROM Ticket INNER JOIN Priority
ON Priority.Name = Ticket.Priority
Note: although using the case keyword is the most straight forward solution for
this problem
this solution may be useful if you will need this priority value in many places at your system

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