insert range of values in sql - 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)
;

Related

How to lookup based on ranged values

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?

using the ids returned from insert into, for record insertion with foreign key

I have a table
monster(id serial, name varchar, primary key(id))
and i have another table
ranged_monster(id_monster integer, distance integer, foreign key(id_monster) references monster)
I want to insert two ranged monsters: one is called 'archer goblin' with a range attack distance of 10, and the another is called 'dragon' with a range attack distance of 50. How can i do this in one instruction?
so far, i have tried this:
the worst way
insert into monster(name) values ('archer goblin'), ('dragon');
insert into ranged_monster(distance) values (10) where name='archer goblin';
insert into ranged_monster(distance) values (50) where name='dragon';
it is bad because the name column allows repetitions, and the retrieved records might be more than one... also having to write the name of the monster twice does not seems like a good habit.
insert into ... returning
if the table ranged_monster only had the column (foreign key) of the id_monster, then i could use this solution:
with the_ids as (
insert into monster(name)
values ('archer goblin'), ('dragon')
returning id
)
insert into ranged_monster(id_monster)
select * from the_ids;
however it does not work, because ranged_monster also has the column distance. Doing so, will insert the ids of the monsters without distance.
possible solutions
create a temporal table with the distances, and then combine this temporal table sequentially with the insert into ... returning's the_ids, and then insert this combined records into the ranged_monster table.
How can i combine two tables as asked in here https://stackoverflow.com/questions/31171253/sql-combine-two-tables ? (it was marked as duplicated, linking to this What is the difference between JOIN and UNION? , but that question is not related to that another question.)
with s(name, distance) as (
values ('archer goblin', 10), ('dragon', 50)
), the_ids as (
insert into monster(name)
select name
from s
returning id, name
)
insert into ranged_monster (id_monster, distance)
select id, distance
from
s
inner join
the_ids using (name)

How to update rows in one column of a Table with increment number in minus in Sybase ASE

I have to update around 40 rows in a column of a table with increment of numbers in minus i.e -1,-2,-3,-4 etc in sybase ASE. Can any one tell how to do
Raj, identity values in ASE cannot be set to maintain a sequence of negative values as you want. So that eliminates the simplest approach.
Let's assume your table looks like this:
CREATE TABLE cars (
id not null,
name varchar(20),
serial int null
)
where serial is the column that may contain NULL values.
You could create a trigger on the table to update serial when it is null.
Alternatively if you are trying to update existing values in this table you could run the following code 40 times:
UPDATE cars
SET serial = -(SELECT COUNT(*) FROM cars WHERE serial IS NULL)
WHERE id = (SELECT MAX(id) FROM cars WHERE serial IS NULL)
Of course, for 40 rows manually updating the values sounds attractive!

SQL: difference of 2 columns in a new column

I have the following SQL table, with a positive column and a negative column, both ints.
positive negative
----------------------
5 3
3 1
10 7
How can I create a third column, e.g. total that is equal to positive - negative. Also, I would like the total column to be updated every time an element of the positive or negative column changes.
How can I do this in SQL?
Edit: I am using MariaDB
make use of computed column as described below
you can create table as
create table table_name ( positive int, negitive int, difference as positive-negitive)
then after creating, if you enter values as
insert into table_name values(3,2)
--no need to enter third column it is called as computed column.
then after inserting the difference will be present in the third column "difference"
Use a virtual computed column as explained here https://mariadb.com/kb/en/mariadb/virtual-computed-columns/
create table table1
(
positive int not null,
negative int not null,
difference int as (positive - negative) virtual
);

Find first available empty column in a row

I have MSSQL table having columns, serves as storage for items for user with certain name. Each user have one record in table Items that looks like that:
Name(string) 1(string) 2(string) 3(string) 4(string) 5(string)
When inserting data, I would need to use the column ("packet") ona by one. Meaning if "1" contains data, use column "2". If "1" is empty, use "1". If only "3" is empty, use "3"/
In other words, I always need to select the lowest empty column to write the data in. If none of these 5 is empty, then do not write data at all.
Could anyone help me with that query? Thank you
The correct way to model this in SQL would be:
CREATE TABLE Tab (
Name varchar(20) not null, --20? Who knows, not specced in question
Idx int not null,
Value varchar(20) not null, --20? Who knows, not specced in question
constraint PK_Tab PRIMARY KEY (Name,Idx),
constraint CK_Tab_Indexes CHECK (Idx between 1 and 5)
)
You could then write your insert as:
INSERT INTO Tab (Name,Idx,Value)
SELECT #Name,COALESCE((select MAX(Idx)+1 from Tab where Name=#Name),1),#Value
where #Name and #Value are the supplied values to insert. If there are already 5 items in the table for a particular name, you'll get a check constraint error and the insert will fail.
This also makes querying for particular values possible, without having to search 5 different columns, and it means that extending to more items in the future far easier (by just changing the check constraint)
I'd had the impression that this was insert only. If deletions are occurring, and you want to reuse items, then the insert query would be:
;WITH Nums(n) as (select 1 union all select 2 union all select 3 union all select 4 union all select 5)
INSERT INTO Tab (Name,Idx,Value)
SELECT #Name,(select MIN(n) from Nums where not exists (select * from Tab where Name=#Name and Idx = n)),#Value
which will now fail because of a not-null constraint once you're up at 5 items. It could be simpler if you already have a numbers table in your database, but I've assumed not above.