This may be a simple question as I have not done any database work for a while!
I have two tables with data like the below
Table 1
Rows with Primary Keys 1,2
Table 2
Rows with Foreign keys 1,2,3,4
I was to be able to perform a DELETE statement which will remove all rows from Table 2 that do not have a corresponding primary key in table 1, which in this case would result in only rows with foreign keys 1 & 2 being left in the table.
I should mention that this is on Android so I am using SQLite and also I am interested in the ease of doing this via a content provider.
Thanks for any help
Try this:
String SQL="DELETE FROM Table2
WHERE (Table2.FQ1,Table2.FQ2) NOT IN (SELECT PK1,PK2 FROM Table1)";
db.SQL(SQL);
But i'm not sure that the (Table2.FQ1,Table2.FQ2) sentence will run into the NOT IN
Related
I am making a restaurant POS app for android and I am trying to decide the best way to model the database for it using Room ORM that ensures maintainability. My database needs, among a lot of other things, to keep a record of all items sold within a transaction/order, as well as a log of the orders themselves and a list of the food products sold within the restaurant.
Considering the following tables (for brevity purposes I only include columns I think relevant to the question and may not illustrate all the information I will need to catalog), I can create a table that includes a log of all the orders ever placed and call it all_orders:
all_orders
-----------
id (PK)
oder_details_id (FK) - referencing the PK from order_details table
date
notes
total
payment_type
I can also create a table that contains all the food products/dishes that the restaurant serves, and we’ll call it all_items:
all_items
---------
id (PK)
name
category
price
No problems there so far, but my current confusion lies here—how do I manage to keep a log of the actual food items sold within an order?
One approach I thought about was to create a table per order number, but creating tables dynamically is already a problem and having 60,000 tables at the end of the year will be a maintainability nightmare.
So my other possible solution is to create a table called order_details that will probably end up with hundreds of thousands of entries per year with the following columns:
order_details
-------------
id (PK)
item_id (FK) - referencing the PK from the all_items table
order_id (FK) - referencing the PK from the all_orders table
quantity_ordered
And when a user wants to pull up an order from say, last week, the program can use a join query that will produce the following to be displayed in the app’s UI:
order
---------
id (PK)
date (from the all_orders table)
name (from all_items)
category (from all_items)
price (from all_items)
total (from all_orders)
payment_type (from all_orders)
I am afraid that the order_details table is just too broad since it will contain hundreds of thousands of entries, and querying it for entries will be sluggish. I'm sure indexing it will help, but is this the correct approach to this problem? If not, is there a better, “best practice” solution? If possible something that focuses on grouping any order and its items together without just dumping all items from all orders into one table. Any help will be most appreciated.
Edit: This question is not a duplicate of this, and while helpful, the supplied link has not provided any additional context on what I am really asking about nor is it entirely relevant to the answer I am after. I have bolded my last original paragraph since my question is really about a how I can model the above data as it isn't clear to me based on my research how to store actual order details attached to an order (many tutorials/similar questions I've come across fail short of thoroughly explaining the aforementioned).
The all_orders table would be superfluous as that is just repeating other data and would be contrary to normalisation.
You probably want a category table rather than repeat data (i.e. normalise categories).
Likewise, you also probably want a payment_type table (again to normalise).
Creating individual tables for orders would probably just create a nightmare.
Price and total aren't they the same? Saying that totals can be derived when extracting the data so there is no need to store such information.
As such the following structure schema may be close to what you want :-
DROP TABLE IF EXISTS item;
DROP TABLE IF EXISTS category;
CREATE TABLE IF NOT EXISTS category (_id INTEGER PRIMARY KEY, category_name TEXT);
CREATE TABLE IF NOT EXISTS item (
_id INTEGER PRIMARY KEY,
item_name TEXT UNIQUE,
category_ref INTEGER REFERENCES category(_id) ON DELETE CASCADE ON UPDATE CASCADE,
item_price REAL
);
DROP TABLE IF EXISTS payment_type;
CREATE TABLE IF NOT EXISTS payment_type (
_id INTEGER PRIMARY KEY,
payment_type TEXT UNIQUE,
surcharge REAL
);
-- NOTE cannot call a table order as it is a keyword (not rea true but have to enclose the name e.g.g [order]).
DROP TABLE IF EXISTS customer_order;
CREATE TABLE IF NOT EXISTS customer_order (
_id INTEGER PRIMARY KEY,
customer_name TEXT,
date TEXT DEFAULT CURRENT_TIMESTAMP,
payment_type_ref INTEGER REFERENCES payment_type(_id) ON DELETE CASCADE ON UPDATE CASCADE
);
DROP TABLE IF EXISTS order_detail;
CREATE TABLE IF NOT EXISTS order_detail (
customer_order_ref INTEGER REFERENCES customer_order(_id) ON DELETE CASCADE ON UPDATE CASCADE,
item_ref REFERENCES item(_id) ON DELETE CASCADE ON UPDATE CASCADE,
quantity
);
Example
The following is native SQL that demonstrates the schema above :-
Part 1 adding (inserting) the data :-
INSERT INTO category (category_name) VALUES
('Fish'),('Beef'),('Chicken'),('Lamb'),('Sea Food')
;
INSERT INTO item (item_name, item_price, category_ref) VALUES
('Fish and Chips',11.30,1),
('Steak and Kidney Pudding',15.45,2),
('Lamb Chops, Mashed Potato and Gravy',17.40,3)
;
INSERT INTO payment_type (payment_type, surcharge) VALUES
('Master Card',0.05),('Visa',0.05),('Cash',0),('American Express',0.15)
;
INSERT INTO customer_order (customer_name, payment_type_ref) VALUES
('Fred',3),
('Mary',1),
('Tom',2),
('Jane',4)
;
INSERT INTO order_detail (customer_order_ref, item_ref, quantity) VALUES
(1,1,2),(1,2,1), -- Fred (id 1) orders 2 Fish and Chips (id 1) and 1 Steak and Kidney (id 2)
(2,3,10), -- Mary orders 10 Lamb chops
(3,2,1),(3,1,1),(3,3,1), -- Tom orders 1 of each
(4,1,1) -- Just Fish and chips for Jane
;
Part 2 - Extracting Useful(perhaps) Data
Here's and example of what you can do with SQL which includes derived data (as suggested above) :-
SELECT
customer_name,
date,
group_concat(item_name) ||'('||quantity||')' AS items,
sum(item_price) AS total_price,
payment_type,
round(sum(item_price) * surcharge,2) AS surcharge,
round((sum(item_price) * surcharge) + sum(item_price),2) AS total_price
FROM customer_order
JOIN order_detail ON customer_order._id = order_detail.customer_order_ref
JOIN item ON order_detail.item_ref = item._id
JOIN payment_type ON customer_order.payment_type_ref = payment_type._id
GROUP BY customer_order._id -- Treats all data for an order as a single row allowing the use of aggregate functions on the groups e.g. sum, group_concat
;
Result
I am using SQLite Database for my application. I have 4 columns- Student_Name,Student_Enroll, Student_Mob, Student_Address in my database. Now I can add new record if and only if one of four column value is different or all values are different. If all column values are same then no new record should be generated.
Can you please guide me to solve this issue?
To enforce that a set of columns must be unique, add a UNIQUE constraint:
create table Students (
/* ID INTEGER PRIMARY KEY, */
Student_Name TEXT,
Student_Enroll TEXT,
Student_Mob TEXT,
Student_Address TEXT,
UNIQUE (Student_Name, Student_Enroll, Student_Mob, Student_Address)
);
This allows new rows only if at least one of the four columns has a different value.
With a plain INSERT, attempting to insert a duplicate row will result in an error. If you simply want to ignore it instead, use INSERT OR IGNORE:
INSERT OR IGNORE INTO Students ...;
Despite of set your column as UNIQUE you also need to resolve the conflict created on each column when you try to insert new data.
To do so, define the behavior to solve the conflict:
"CREATE TABLE table (your columns here...(UNIQUE unique colums here...) ON CONFLICT REPLACE);"
During Create Database line insert UNIQUE ...for each column to insert only unique record.
Solution 1: (Simple)
Define all columns as unique:
create table TableName (id integer primary key autoincrement,
Student_Name text not null unique,
Student_Enroll text not null unique,
Student_Mob text not null unique);
You can add Student_Address as well, if you need to
Solution 2: (bit complex)
Use AND Operator with WHERE clause
INSERT INTO TableName (Student_Name, Student_Enroll, Student_Mob)
SELECT varStudentName, varStudentEnroll, varStudentMob
WHERE NOT EXISTS(SELECT 1 FROM TableName WHERE Student_Name = varStudentName OR Student_Enroll = varStudentEnroll OR Student_Mob = varStudentMob );
//If a record already contains a row, then the insert operation will be ignored.
You can find more information at the sqlite manual.
Live Example:
Open SQLite Online
Paste following code:
INSERT INTO demo (id,name,hint)
SELECT 4, 'jQuery', 'is a cross-platform JavaScript library designed to simplify the client-side scripting of HTML'
WHERE NOT EXISTS(SELECT 1 FROM demo WHERE name = 'jQuery' OR hint = 'is a cross-platform JavaScript library designed to simplify the client-side scripting of HTML' );
SELECT * from demo
Hit RUN
This won't insert 4th record and if you modify both values of WHERE clause then record will be inserted.
I have a table named groups, and I want to rename one of its columns. Is was ok, so far. I know sqlite doesn't support renaming columns, so I did:
ALTER TABLE groups RENAME to tmp_groups;
CREATE TABLE groups(
_ID integer primary key autoincrement,
new_column_name integer
);
INSERT INTO groups(_ID, new_column_name) SELECT _ID, old_column_name FROM tmp_groups;
DROP TABLE tmp_groups;
But, when I drop the table tmp_groups, the table members, that had a foreign key with ON DELETE CASCADE has its records deleted as well, so I had to do the following:
Create a table tmp_members with the same columns as members, and without the foreign key;
Insert the records from members in tmp_members;
Drop the table members;
Run the code from the first part (with the groups table);
Re-create the table members with its foreign key;
Insert in members data from tmp_members;
Man, that was tiring! Its too much code to simply rename a column;
Is there any simpler way to handle this constraint problem, or is this the "sqlite way"?
For historical reasons, SQLite allows to disable foreign key constraints (and this is even the default currently).
Just run PRAGMA foreign_keys = off before doing the groups table stuff.
It would also be possible to rename a column by using PRAGMA writable_schema, but you should do this only if you know what you're doing.
I have an Android app with SQLite and use the following line when creating the table:
_id integer primary key autoincrement
My question is, can I do an update on this row and change the _id if my user wants to? So, for example, I'll have two records in the DB:
1 My First Record
2 My Second Record
Now after my update, I'd like something like:
1 My First Record
38 My Second Record
Will SQLite allow this and easily adapt? i.e. the next primary key autocreated will be #39?
As documented, changing the _id is allowed and will update the autoincrement sequence properly.
Just try it:
$ sqlite3
SQLite version 3.7.16 2013-01-11 09:58:54
Enter ".help" for instructions
Enter SQL statements terminated with a ";"
sqlite> CREATE TABLE MyTable(_id INTEGER PRIMARY KEY AUTOINCREMENT);
sqlite> INSERT INTO MyTable DEFAULT VALUES;
sqlite> INSERT INTO MyTable DEFAULT VALUES;
sqlite> UPDATE MyTable SET _id = 38 WHERE _id = 2;
sqlite> INSERT INTO MyTable DEFAULT VALUES;
sqlite> SELECT * FROM MyTable;
1
38
39
if you want to do such things, you'll have to manage the autoincrement by yourself. SQLite will not allow you to change the value of autogenerated field. Another approach is to add another field and use it instead.
Also, if there is some kind of workaround, I can assure you it's not a good practice.
You should never modify primary keys this way, e.g. think of foreign keys etc. Sqlite won't allow this.
Apart from that, the auto increment counter is stored in a seperate table and has no idea what you are doing. Even if it was allowed to change the primary key, the counter would remain unaffected.
friends,
I am doing an Android project in my company, still some small work is remaining, I need your help to complete the task.
The problem is...
I have created two tables in which, table1 has an empty column, for purpose for saving name...
The table2 has a list of names, the objective is only the names from this list should be should be saved in the table1's empty column other than that it shouldn't accept any of the name typed manually.
You appear to want to make the list of names a validation: if the user wishes to save a name to table1, the name must already exist in table2.
Typically this would be done as in the following example, in which only the products listed in PRIZEPRODUCTS can be entered into PRIZEWINNERS table: someone could not win a Boat, for example, given the data below:
PRIZEPRODUCTS
id
productname
1|TV
2|iPad
3|backpack
PRIZEWINNERS
id
productid
winner
ALTER TABLE PRIZEWINNERS
ADD CONSTRAINT PRIZEWINNERS_PRIZEPRODUCTS_FK
FOREIGN KEY(productid) REFERENCES PRIZEPRODUCTS(id)
SQLite doesn't create the foreign key using ALTER TABLE but as part of the create-table statement. See here for the syntax. For enabling foreign key support in Android (2.2), see here.
Now, you can establish the foreign key on the [productname] column if [productname] were the key of PRIZEPRODUCTS. In other words, you could make person-name the key of the table rather than having a PersonID. But if that name is changed in the validation table, it can break the foreign key relationship, unless ON UPDATE CASCADE is enabled, but I am not sure if this is supported in Android.
I hope below query will work for you.
insert into table1(name) values (select name from table2 where id=?).
Thanks.