How to lookup based on ranged values - sql

I have a table like:
id name
001to005 ABC
006to210 PQR
211to300 XYZ
This is not the final table i can make it any how i want...so i would like to lookup on this data on id and extract name like if id is in range of 001-005 then ABC and if id is in range 006-010 .... then name XYZ.
My approach would be, store id as regular expression in table like this:
id name
[0][0][1-5] ABC
[0-2][0-9][0-9] PQR
[2-3][0-9][0-9] XYZ
and then query:
select * from table where '004' ~ id
This query will return ABC which is correct but when range gets bigger my input value can lie on both 2nd and 3rd row.
For Eg:
select * from table where '299' ~ id
this query will result in 2 rows,so my question is what reg exp to use to make it more restrictive or is there any other approach to solve this:

Do not store regular expressions for simple ranges, that would be extremely expensive and cannot use an index: every single expression in the table would have to be evaluated for every query to satisfy conditions.
You could use range types like #a_horse commented. But while you don't need the added functionality for range types this simple layout is smaller and faster:
CREATE TABLE tbl (
id_lo int NOT NULL
, id_hi int NOT NULL
, name text NOT NULL
);
INSERT INTO t VALUES
( 1, 5, 'ABC')
, ( 6, 210, 'PQR')
, (211, 300, 'XYZ');
CREATE UNIQUE INDEX foo ON t (id_lo, id_hi DESC);
Two integer occupy 8 bytes, int4range value occupies 17 bytes. Size matters in tables and indexes.
Query:
SELECT * FROM tbl
WHERE 4 BETWEEN id_lo AND id_hi;
Lower (id_lo) and upper (id_hi) bounds are included in the range like your sample data suggests.
Note that range types exclude the upper bound by default.
Also assuming that leading zeros are insignificant, so we can operate with plain integer.
Related:
PostgreSQL daterange not using index correctly
Optimizing queries on a range of timestamps (two columns)
Find overlapping date ranges in PostgreSQL
To enforce distinct ranges in the table:
Preventing adjacent/overlapping entries with EXCLUDE in PostgreSQL
You still don't need a range type in the table for this:
Postgres: How to find nearest tsrange from timestamp outside of ranges?

Related

insert range of values in sql

CREATE TABLE RECHARGE(
R_LVL VARCHAR(20) NOT NULL PRIMARY KEY,
AMOUNT NUMBER,
POINTS_1 NUMBER
);
insert into RECHARGE
VALUES ('S1',(2950-4950),250);
i have the code above, and im trying to insert a range for the values in attribute amount, like this :
s1 : 2950-4950
s2: 5000-9950
s3: 10000-30000
so each lvl has it's own range of data, is it possible ?
Number datatype cannot store special characters like -. You have to make it either VARCHAR or store 2 rows with single number like -
insert into RECHARGE
VALUES ('S1',2950,250);
insert into RECHARGE
VALUES ('S1',4950,250);
Or you can use 2 colums also like FROM-TO to show your date range like -
CREATE TABLE RECHARGE(
R_LVL VARCHAR(20) NOT NULL PRIMARY KEY,
AMOUNT_FROM NUMBER,
AMOUNT_TO NUMBER,
POINTS_1 NUMBER
);
insert into RECHARGE
VALUES ('S1',2950,4950,250);
Then in SELECT query you can generate the result like you want.
Range consists of 2 values, store it in 2 columns.
The expression (2950-4950) equals to -2000 (2950 minus 4950).
You should create two columns or have only just one of the boundaries of the ranges stored.
I suggest to store both values, because it is easier to query:
CREATE TABLE RECHARGE(
R_LVL VARCHAR(20) NOT NULL PRIMARY KEY,
LOWER_BOUNDARY NUMBER,
UPPER_BOUNDARY NUMBER,
POINTS_1 NUMBER
);
You need to decide if the boundaries are closed (<=, >=) or open (<, >).
So you can query them like this:
SELECT * FROM Recharge WHERE LOWER_BOUNDARY <= 2000 AND UPPER_BOUNDARY > 2000
Where 2000 is the value you are trying to find the range for.
You can make this thing more bulletproof by adding a few constraints:
CHECK constraint to make sure that LOWER_BOUNDARY is lower than
UPPER_BOUNDARY.
UNIQUE on LOWER_BOUNDARY to eliminate repetition
FOREIGN KEY to reference the UPPER_BOUNDARY from the previous range
for the current ones LOWER_BOUNDARY (boundaries are continuous)
it is possible
use the next query
insert into RECHARGE
VALUES ('S1',(2950-4950),250)
('S2',(5000-9950),250)
('S3',(10000-30000),250)
;

How can I alter data type and datas in it, numbers separated with comma

I have table , that has field named AMOUNT , amount takes number, table has 1.4m records, i need to upgrade them all . I would like the change NUMBER to varchar and make look like amount datas comma separated , eg: 76543-> 76,543. How can I able to do it?
1 - Create the new column at the end of the table.
2 - Run an update to populate the new table column
(in this step for thousand seperataor look Thousand Seperator function in oracle? )
3 - Drop the old table column
4 - Re-name the new column to the original column name
i need to upgrade them all
Don't; if you have a numeric value then store it as a NUMBER.
I would like the change NUMBER to varchar and make look like amount datas comma separated , eg: 76543-> 76,543. How can I able to do it?
Just change how you are displaying the value rather than changing how you are storing the value.
If you have the table and data:
CREATE TABLE table_name ( amount NUMBER(12,0) );
INSERT INTO table_name ( amount ) VALUES ( 76543 );
If you want to do it in a SELECT statement then use TO_CHAR and include sufficient digits to format the largest number you can hold:
SELECT amount,
TO_CHAR(amount, 'FM999G999G999G990') AS formatted_amount
FROM table_name;
Outputs:
AMOUNT
FORMATTED_AMOUNT
76543
76,543
If you want to do that in the table then add a virtual column:
ALTER TABLE table_name
ADD formatted_amount VARCHAR2(16)
GENERATED ALWAYS AS ( TO_CHAR(amount, 'FM999G999G999G990') );
Then, after adding the virtual column:
SELECT * FROM table_name;
Outputs:
AMOUNT
FORMATTED_AMOUNT
76543
76,543
db<>fiddle here
You can use to_char():
select to_char(col, 'FM999,990;')

About dual table structure in Oracle SQL *Plus

We know that "Dual" is a temporary table which exactly contains 1 column whose name is "dummy" which is of "varchar2(1)" type that has 1 single row. The value of that record is "X". The varchar2 has size of 1 which means it should not allow more than a single character.
Now my question is: If it is of varchar2 type, then why it can hold any datatype temporarily AND If we insert characters more than 1 (size), how this is possible for it (dual) to accept it ? Example:
SQL> desc dual
Name Null? Type
------------------------------- -------- ----
DUMMY VARCHAR2(1)
SQL> select sysdate from dual;
SYSDATE
---------
22-JAN-13
SQL> select 5*5 result from dual;
RESULT
---------
25
SQL> select 'Ankita' as "Name" from dual;
Name
------
Ankita
Clearly "SYSDATE" is of DATE type (not varchar2), "RESULT" is of NUMBER type (not varchar2). Also 'Ankita' is more than 1 single character, i.e., 6 characters. This should contradict the structure that varchar2 is holding only 1, and supporting 6 too..
The types and sizes of columns only come into play when you extract the data from them. It's true that if you select the dummy column, it will only give you what is in that column.
But, when you're selecting something unrelated to the column data, such as 5*5 or 'Ankita', they have no such restrictions.
I think your misunderstanding stems from your snippet:
why it can hold any datatype temporarily AND If we insert characters more than 1 (size), how this is possible for it (dual) to accept it ?
The truth is, the column doesn't accept it. Selecting 5*5 does not attempt to place that data into the table somewhere, it just evaluates it as-is. It's no different to having a table with:
users:
id
name
and performing:
select 42, 3.14159, id, name from users
For every row selected from, you will see the constant values 42 and 3.14159, alongside the data extracted from the two specified columns.

Select statement in SQLite recognizing row number

I want to write SQLite statement something like this:
SELECT * FROM Table WHERE RowNumber BETWEEN 1 AND 10;
but i don't have such column RowNumber. I have primary key in my table. But is there row number by default that i could use ?
Also i am searching info about writing more complicated SQLite statement. So if you have some links in bookmarks please share.
Thanks.
You want to use LIMIT and OFFSET
SELECT * FROM Table LIMIT 10 OFFSET 0
Which can also be expressed with the following shorthand syntax
SELECT * FROM Table LIMIT X,Y
Where X represents the offset, which is exclusive, and Y represents the quantity, so for example
SELECT * FROM Table LIMIT 50,50
Would return rows 51-100
The automatically-created rowid for a table can be accessed by a few different names. From the SQLite documentation:
Every row of every SQLite table has a 64-bit signed integer key that uniquely identifies the row within its table. This integer is usually called the "rowid". The rowid value can be accessed using one of the special case-independent names "rowid", "oid", or "_rowid_" in place of a column name.
SELECT * FROM Table WHERE ROWID BETWEEN 1 AND 10;

Cannot lookup row in database by UUID RAW(32)

I have the following table:
create table Mike_Test
( Id raw(32) default sys_guid() not null primary key,
Value varchar2(80) not null unique
);
I now insert into this table:
INSERT INTO Mike_Test (VALUE) VALUES ('Blah');
I can confirm there's a new row in there:
select * from Mike_Test;
And I see:
08364fc81419429d83c9bcedb24a9a57 Blah
Now I try to SELECT that row with:
select * from Mike_Test WHERE ID='08364fc81419429d83c9bcedb24a9a57';
However, I get zero rows and zero errors. What am I doing wrong?
It's a RAW datatype, you have to query like this:
select .... where id=hextoraw('08364fc81419429d83c9bcedb24a9a57') ....
I think you might need to use the HEXTORAW function around your ID value. This function converts a hex string into the corresponding RAW value.
In other words, your query should look like the following:
select * from Mike_Test WHERE ID=HEXTORAW('08364fc81419429d83c9bcedb24a9a57');
Heh, I did some more toying and the answer is pretty silly. It seems Oracle will implicitly cast a string to a GUID (like a normal human being would expect), but the casts are case-sensitive! Which makes absolutely zero sense if you're parsing hexidecimal numbers and casing isn't relative to your base. The reason this was throwing me off was because I was using Aqua Data Studio, which for some reason displays GUIDs in all lower case.
The following is from SQLPlus, which handles the output correctly:
SQL> select * from Mike_Test;
ID
--------------------------------
VALUE
--------------------------------------------------------------------------------
4FBD50C370BC4A7F85E3DF034D120930
Blah
SQL> select * from Mike_Test WHERE ID='4fbd50c370bc4a7f85e3df034d120930';
no rows selected
SQL> select * from Mike_Test WHERE ID='4FBD50C370BC4A7F85E3DF034D120930';
ID
--------------------------------
VALUE
--------------------------------------------------------------------------------
4FBD50C370BC4A7F85E3DF034D120930
Blah
Oracle gets sillier each day I use it, I swear.
Adding to my answer (I didn't want to EDIT my original answer since this is such a tangent):
There's huge performance differences between the implicit cast I was trying to do, and the recommended HEXTORAW approach. The query plan for the implicit cast looks something like:
SELECT STATEMENT 3.0 3 37877 1 52 3 ALL_ROWS
TABLE ACCESS (FULL) 3.0 3 37877 1 52 1 TPMDBO MIKE_TEST FULL TABLE 1
And HEXTORAW is:
SELECT STATEMENT 1.0 1 15463 1 52 1 ALL_ROWS
TABLE ACCESS (BY INDEX ROWID) 1.0 1 15463 1 52 1 TPMDBO MIKE_TEST BY INDEX ROWID TABLE 1
INDEX (UNIQUE SCAN) 1.0 1 8171 1 1 TPMDBO SYS_C007969 UNIQUE SCAN INDEX (UNIQUE) 1
As you can see, the HEXTORAW approach will hit the primary key index on the table. This is because the implicit conversion will honor the "string" type of the right operand (the GUID I specify), and convert all the GUIDs in the database to strings. Definitely something to consider.