Calculate total upon inserting data in SQLite - sqlite

I would like to add the price and amount as total upon inserting it into SQLite db.
I've tried the below, but it didn't work.
INSERT INTO product (name, price, amount, total) VALUES (?, ?, ?, (price + amount))

The classic approach is to use a view that computes the values of extra columns, and query it instead of the base table:
sqlite> CREATE TABLE raw_product(name, price, amount);
sqlite> CREATE VIEW product(name, price, amount, total) AS SELECT name, price, amount, price * amount FROM raw_product;
sqlite> INSERT INTO raw_product VALUES ('Widget', 1.50, 100);
sqlite> SELECT * FROM product;
name price amount total
------ ----- ------ -----
Widget 1.5 100 150.0
A newer way (Since Sqlite 3.31) is to use a generated column instead, which lets you include computed values in the table itself instead of wrapping it in a view:
sqlite> CREATE TABLE product(name, price, amount, total AS (price * amount));
sqlite> INSERT INTO product VALUES ('Widget', 1.50, 100);
sqlite> SELECT * FROM product;
name price amount total
------ ----- ------ -----
Widget 1.5 100 150.0
There are options you can play with to control if the value is computed each time it's requested, or if it's materialized in the database and updated when other fields of the given row are. See the linked documentation for details.

Related

SQL error or missing database (near "," syntax error)

I have this query with syntax error. Can you help me find the error please?
I spend couple of hour and I can't solve it. Thanks.
String sql = " INSERT INTO appointments (patient_firstname, patient_surname, fees, time, date, doctor)"
+ " SELECT (time, date, doctor)"
+ " WHERE NOT EXISTS (SELECT * FROM appointments WHERE time = ?)";
pst=conn.prepareStatement(sql);
pst.setString(1,txt_firstname.getText());
pst.setString(2,txt_surname.getText());
pst.setString(3, txt_fee.getText());
pst.setString(4, (String) cbox_time.getSelectedItem());
pst.setString(5,txt_date.getText());
pst.setString(6, (String) cbox_doctors.getSelectedItem());
pst.executeUpdate();
The issues with your INSERT statement includes :-
You appear to be attempting an INSERT SELECT where the VALUES to be inserted are obtained from the SELECT clause, but you are trying to mix this with VALUES. The two types cannot be combined.
You are trying to select using columns within parenthesises and anyway those three columns WILL NOT match the 6 columns required for the insert.
You only have 1 ? to be bound, you are trying to bind 6.
I'd suggest that you could simplify matters by defining the table with a UNIQUE constraint on the doctor, date and time columns. Then use a standard INSERT OR IGNORE with the VALUES clause.
e.g. consider the following demonstration SQL :-
DROP TABLE IF EXISTS appointments;
CREATE TABLE IF NOT EXISTS appointments (patient_firstname TEXT, patient_surname TEXT, fees REAL, time TEXT, date TEXT, doctor INTEGER, UNIQUE (doctor, date, time));
INSERT OR IGNORE INTO appointments VALUES ('Fred','Bloggs',100.50,'10:00','2019-01-01',1);
INSERT OR IGNORE INTO appointments VALUES ('Mary','Smith',100.50,'10:00','2019-01-01',2);
INSERT OR IGNORE INTO appointments VALUES ('Sue','Bell',100.50,'10:00','2019-01-01',2);
SELECT * FROM appointments;
This results in :-
i.e Sue Bell's appointment has not been added because of the UNIQUE constraint conflict, which has been ignored so there is no error.
The log being :-
DROP TABLE IF EXISTS appointments
> OK
> Time: 0.177s
CREATE TABLE IF NOT EXISTS appointments (patient_firstname TEXT, patient_surname TEXT, fees REAL, time TEXT, date TEXT, doctor INTEGER, UNIQUE (doctor, date, time))
> OK
> Time: 0.084s
INSERT OR IGNORE INTO appointments VALUES ('Fred','Bloggs',100.50,'10:00','2019-01-01',1)
> Affected rows: 1
> Time: 0.083s
INSERT OR IGNORE INTO appointments VALUES ('Mary','Smith',100.50,'10:00','2019-01-01',2)
> Affected rows: 1
> Time: 0.084s
INSERT OR IGNORE INTO appointments VALUES ('Sue','Bell',100.50,'10:00','2019-01-01',2)
> Affected rows: 0
> Time: 0s
SELECT * FROM appointments
> OK
> Time: 0.003s

SQLite search two tables

In a SQLite database, I have created two tables:
CREATE Table Master (ItemID VARCHAR PRIMARY KEY, Property VARCHAR)
CREATE Table Counter (OtherID VARCHAR PRIMARY KEY, ItemID VARCHAR)
Records on table Master:
* ItemID: Book, Property: large
* ItemID: Table, Property: green
Records on table Counter:
* OtherID: random1, ItemID: Book
* OtherID: random2, ItemID: Book
* OtherID: random3, ItemID: Book
The column ItemID on table Master has the same contents as the same-named column on table Counter.
What is the correct SQL select statement to get all rows from table Master sorted by the number of their records in table Counter ?
In this case, row "Book" has three counts in table Counter and should be listed on first position, while row "Table" has no counts and should be the second result.
I know how to do this on one table but never managed to get a SQL select statement working that spans two tables.
Any help is appreciated.
By the way: I cannot change the table structure; so not sure if there would be something better, but I have to work with the tables as they are.
attach to two different databases
access tables with "db?." in front
join both tables on the common semantic, i.e. the ItemId
left join to get the "empty" lines, too, with "0" count
make groups which represent the lines you want in the output, i.e. also by ItemId
grouping allows using the aggregate function "count()"
order according to desired output, i.e. by count, but descending to get "3" first
select the ItemId and the property to match desired output
Code:
attach 'master.db' as dbm;
attach 'counter.db' as dbc;
select a.ItemId, property
from dbm.Master a LEFT JOIN dbc.Counter b
using (ItemId)
group by a.ItemId
order by count(OtherId) desc;
Tested with :
echo .dump | sqlite3 counter.db
BEGIN TRANSACTION;
CREATE TABLE Counter (OtherID VARCHAR PRIMARY KEY, ItemID VARCHAR);
INSERT INTO Counter VALUES('random1','book');
INSERT INTO Counter VALUES('random2','book');
INSERT INTO Counter VALUES('random3','book');
COMMIT;
echo .dump | sqlite3 master.db
BEGIN TRANSACTION;
CREATE TABLE Master (ItemID VARCHAR PRIMARY KEY, Property VARCHAR);
INSERT INTO Master VALUES('book','large');
INSERT INTO Master VALUES('table','green');
COMMIT;
Output:
book|large
table|green
If I understand you, I think this should work:
SELECT M.ItemId, Property
FROM Master M
LEFT JOIN Counter C
ON M.itemid=C.itemid
GROUP BY C.itemid
ORDER BY COUNT(C.itemid) DESC;

Drop the entries with lowest values in SQLite in place

Let's consider the following table
CREATE TABLE t ( param INTEGER, te TEXT );
If we want to sort it by param (descending) and show its first 20 (biggest) entries we can do
SELECT * FROM t ORDER BY param DESC LIMIT 20;
If we want to save only those 20 rows we could do
ALTER TABLE t RENAME TO t_bak;
CREATE TABLE t (param INTEGER, te TEXT);
INSERT INTO t SELECT * FROM t ORDER BY param DESC LIMIT 20;
DROP t_bak
Can this be done in place (or at least in a less clumsy manner)?
In SQLite (and in any other database that understands standard SQL), this can be done with a simple subquery:
DELETE FROM t
WHERE param NOT IN (SELECT param
FROM t
ORDER BY param DESC
LIMIT 20);

Plsql wont insert because of duplicate records

So I am trying to fill a table that connect two different tables by randomly selection the id from a products table x amounts of time and then putting this id together with an id of the shops table. And then repeating this for all id from the shopa table. This way every shop gets a random amount of products; However because sometimes my randomly selected ID is the same as an id that is already in the Table for example
shop = 1 ||product =34
shop = 1 || product =20
shop = 1 || product =34
How can i prevent this from happening the code that i am trying to execute is
create or replace PROCEDURE GENERATEPRODUCTS
AS
PRODUCTTEMP NUMBER;
NROFPRODUCTS NUMBER;--total number of available products
NROFWINKELS NUMBER;--total number of shops
MAXNROFPRODUCT NUMBER;-- the maximum amount of products to be inserted
PRODUCTPERCENTAGEZONDER NUMBER;-- random percentage that will be added to 90%
PRODUCTPERCENTAGEMET NUMBER;-- total percentage of to be inserted products
WINKELS NUMBER;--counter for looping through shops
PRODUCTIDTEMP NUMBER;--the id of the product that needs to be inserted
BEGIN
PRODUCTIDTEMP :=1;
WINKELS := 1;
PRODUCTPERCENTAGEMET :=0;
PRODUCTPERCENTAGEZONDER := 0;
select count(ID)
into NROFWINKELS
FROM WINKEL;
select count(ID)
into NrofWinkels
FROM WINKEL;
select count(ID)
into NROFPRODUCTS
FROM PRODUCT;
select DBMS_RANDOM.VALUE(0,10) into PRODUCTPERCENTAGEZONDER FROM DUAL;
PRODUCTPERCENTAGEMET := (90+PRODUCTPERCENTAGEZONDER)*.010;
MAXNROFPRODUCT:=ROUND(NROFPRODUCTS*PRODUCTPERCENTAGEMET);
WHILE WINKELS <= NROFWINKELS
LOOP
WHILE MAXNROFPRODUCT<=NROFPRODUCTS
LOOP
SELECT ID
INTO PRODUCTTEMP
FROM(
SELECT ID
FROM PRODUCT
ORDER BY DBMS_RANDOM.VALUE)
WHERE ROWNUM=1;
INSERT INTO WINKEL_COUPON
("ID",WINKEL_ID,COUPON_ID)
VALUES
(PRODUCTIDTEMP,WINKELS,PRODUCTTEMP);
PRODUCTIDTEMP := PRODUCTIDTEMP+1;
END LOOP;
WINKELS := WINKELS+1;
END LOOP;
END;
In this code i want that every shop gets atleast 90% of the available products;
I think you have to put a CONSTRAINT on the table.
That way you can make sure that some columns have a unique value in them.
For example, if you want the value for ColumnShop and ColumnProduct together to be unique in the table you have to put a contstraint on the table that looks something like this:
CONSTRAINT <NameTheConstraint> PRIMARY KEY(Shop, Product)
By doing this, the table will only accept values to be inserted in the table that are unique.
So Shop = 1, Product = 34 will be inserted if that combination doesn't exist yet. If it does it will atomaticly raise an error that a constraint has been violated.

SQLITE fill value with unique random table

I want to create a table with a field that is unique and limited to a certain value. Lets say that the limit is 100, the table is full, I remove a random row, and when I create a new row it has the value that was freed before.
It doesn't need to be the fastest thing in the world (the limit is quite small), I just want to implement it in a DB.
Any ideas?
Create one more column in main table, say deleted (integer, 0 or 1). When you need to delete with certain id, do not really delete it, but simply update deleted to 1:
UPDATE mytable SET deleted=1 WHERE id = <id_to_delete>
When you need to insert, find id to be reused:
SELECT id FROM mytable WHERE deleted LIMIT 1
If this query returns empty result, then use INSERT to create new id. Otherwise, simply update your row:
UPDATE mytable SET deleted=0, name='blah', ... WHERE id=<id_to_reuse>
All queries reading from your main table should have WHERE constraint with NOT deleted condition:
SELECT * FROM mytable WHERE NOT deleted
If you add index on deleted, this method should work fast even for large number of rows.
This solution does everything in a trigger, so you can just use a normal INSERT.
For the table itself, we use an autoincrementing ID column:
CREATE TABLE MyTable(ID INTEGER PRIMARY KEY, Name);
We need another table to store an ID temporarily:
CREATE TABLE moriturus(ID INTEGER PRIMARY KEY);
And the trigger:
CREATE TRIGGER MyTable_DeleteAndReorder
AFTER INSERT ON MyTable
FOR EACH ROW
WHEN (SELECT COUNT(*) FROM MyTable) > 100
BEGIN
-- first, select a random record to be deleted, and save its ID
DELETE FROM moriturus;
INSERT INTO moriturus
SELECT ID FROM MyTable
WHERE ID <> NEW.ID
ORDER BY random()
LIMIT 1;
-- then actually delete it
DELETE FROM MyTable
WHERE ID = (SELECT ID
FROM moriturus);
-- then change the just inserted record to have that ID
UPDATE MyTable
SET ID = (SELECT ID
FROM moriturus)
WHERE ID = NEW.ID;
END;

Resources