I have created the following tables in SQLite3 to tag items (after reading this great response). The tags are saved in the table Tags and the ItemTags will show the relation between one item (from the table Items) and one or more tags (from the table Tags).
CREATE TABLE Items ItemID INTEGER PRIMARY KEY, Title TEXT, Comment TEXT;
CREATE TABLE Tags TagID INTEGER PRIMARY KEY, Title TEXT;
CREATE TABLE ItemsTags ItemID INTEGER, TagID INTEGER;
When submitting a new row, the user will enter a title and a comment (which will be saved in the table Items) and chose from one or more tags (which are chosen/added from/to the table Tags). So far, I've for instance managed to do this:
INSERT INTO Items (Title, Comment) VALUES ('First title', 'First comment');
I want the column ItemID to be a INTEGER PRIMARY KEY, but at the same time, I want to access that value in the same call. Say, for instance, that my table Tags has the following layout:
TagID | Title
------|----------
1 | First tag
2 | Second tag
and that I want to tag the above mentioned statement ("First title", which has the TitleID 1) with TagID 1 and 2, and save the relation to the table ItemsTags. After I'm done, I want the following changes to be made:
Table: Items
TitleID | Title | Comment
--------|-------------|--------------
1 | First title | First comment
Table: Tags
TagID | Title
------|----------
1 | First tag
2 | Second tag
Table: ItemsTags
TagID | ItemID
------|---------
1 | 1
2 | 1
How can I achieve this? Thanks in advance!
You cannot insert rows into two separate tables with a single call to the database, nor can you insert two rows into the same table with a single call. You will need four in this case:
INSERT INTO Items (Title, Comment) VALUES ('First title', 'First comment');
SELECT last_insert_rowid() -- To get last inserted id
INSERT INTO ItemTags (TagID, ItemID) VALUES (1, :LastID)
INSERT INTO ItemTags (TagID, ItemID) VALUES (2, :LastID)
If you place them all inside a transaction, they will all get committed at the same time, with only a single lock placed on the database file.
Related
I'm currently designing my tables. i have three types of user which is, pyd, ppp and ppk. Which is better? inserting data in one row or in multiple row?
which is better?
or
or any suggestion? thanks
I would go for 3 tables:
user_type
typeID | typeDescription
Main_table
id_main_table | id_user | id_type
table_bhg_i
id_bhg_i | id_main_table | data1 | data2 | data3
Although I see you are inserting IDs for each user , I don't quite understand how are are you going to differentiate between the users , had I designed this DB , I would have gone for tables like
tableName: UserTypes
this table would contain two field first would be ID and second would be type of user
like
UsertypeID | UserType
the UsertypeID is a primary key and can be auto increment , while UserType would be your users pyd ,ppk or so on . Designing in this way would give you flexibility of adding data later on in the table without changing the schema of the table ,
the next you can edit a table for generating multiple users of a particular type, this table would refer the userID of the previous table , this will help you adding new user easily and would remove redundancy
tableName:Users
this table would again contain two fields, the first field would be the id call and the secind field would be the usertypeId try
UserId |UserName | UserTypeID
the next thing you can do is make a table to insert the data , let the table be called DataTable
tableName: DataTable
this table will contain the data of the users and this will reference then easily
DataTabID | DataFields(can be any in number) | UserID(refrences Users table)
these tables would be more than sufficient .If doubts as me in chatbox
The short version is I'm trying to map from a flat table to a new set of tables with a stored procedure.
The long version: I want to SELECT records from an existing table, and then for each record INSERT into a new set of tables (most columns will go into one table, but some will go to others and be related back to this new table).
I'm a little new to stored procedures and T-SQL. I haven't been able to find anything particularly clear on this subject.
It would appear I want to something along the lines of
INSERT INTO [dbo].[MyNewTable] (col1, col2, col3)
SELECT
OldCol1, OldCol2, OldCol3
FROM
[dbo].[MyOldTable]
But I'm uncertain how to get that to save related records since I'm splitting it into multiple tables. I'll also need to manipulate some of the data from the old columns before it will fit into the new columns.
Thanks
Example data
MyOldTable
Id | Year | Make | Model | Customer Name
572 | 2001 | Ford | Focus | Bobby Smith
782 | 2015 | Ford | Mustang | Bobby Smith
Into (with no worries about duplicate customers or retaining old Ids):
MyNewCarTable
Id | Year | Make | Model
1 | 2001 | Ford | Focus
2 | 2015 | Ford | Mustang
MyNewCustomerTable
Id | FirstName | LastName | CarId
1 | Bobby | Smith | 1
2 | Bobby | Smith | 2
I would say you have your OldTable Id to preserve in new table till you process data.
I assume you create an Identity column Id on your MyNewCarTable
INSERT INTO MyNewCarTable (OldId, Year, Make, Model)
SELECT Id, Year, Make, Model FROM MyOldTable
Then, join the new table and above table to insert into your second table. I assume your MyNewCustomerTable also has Id column with Identity enabled.
INSERT INTO MyNewCustomerTable (CustomerName, CarId)
SELECT CustomerName, new.Id
FROM MyOldTable old
JOIN MyNewCarTable new ON old.Id = new.OldId
Note: I have not applied Split of Customer Name to First Name and
Last Name as I was unsure about existing data.
If you don't want your OldId in MyNewCarTable, you can DELETE it
ALTER TABLE MyNewCarTable DROP COLUMN OldId
You are missing a step in your normalization. You do not need to duplicate your customer information per vehicle. You need three tables for 4th Normal form. This will reduce storage size and more importantly allow an update to the customer data to take place in one location.
Customer
CustomerID
FirstName
LastName
Car
CarID
Make
Model
Year
CustomerCar
CustomerCarID
CarID
CustomerID
DatePurchaed
This way you can have multiple owners per car, multiple cars per owner and only one record needs to be updated per car and or customer...4th Normal Form.
If I am reading this correctly, you want to take each row from table 1, and create a new record into table A using some of that row data, and then data from the same original row into Table B, Table C but referencing back to Table A again?
If that's the case, you will create TableA with an Identity and make thats the PK.
Insert the required column data into that table and use the #IDENTITY to retrieve the last identity value, then you will insert the remaining data from the original table into the other tables, TableB, TableC, etc. and use the identity you retrieved from TableA as the FK in the other tables.
By Example:
Table 1 has columns col1, col2, col3, col4, col5
Table A has TabAID, col1, col2
Table B has TabBID, TabAID, col3
TableC has TabCID, TabAID, col4
When the first row is read, the values for col1 & col2 are inserted into TableA.
The Identity is captured from that row inserted, and then value for col3 AND the identity are entered into TableB, and then value for col4 AND the identity are entered into TableC.
This is a standard data migration technique for normalizing data.
Hope this assists,
I have a table with two columns, where none of the columns is unique. I need to auto increment the column number separately for each user.
user | number
1 | 1
2 | 1
1 | 2
3 | 1
The only idea I could come up with is to search for the last number used and manually increment by one. Is there a more efficient way?
Instead of the number field, You can create an auto increment field in the table (I call it id), and get the desired number via a query:
first adding id:
alter table table_name add id int not null IDENTITY(1,1)
you do not need the number field anymore:
alter table table_name drop column number
The query to get number (you can use it to create a view):
select user,
row_number() over(partition by user order by id) as number
from table_name
Search for a user maximum and increment it.
INSERT INTO YOUR_TABLE (user, number)
SELECT (MAX(number) + 1)
FROM YOUR_TABLE
WHERE user = USER_ID
The term of auto-increment covers only the primary key auto-increment. See this page, for more information.
You can do it like in two query:
userno - id of user in integer you want to insert
first write(query 1) i.e if userno already there in table:
insert into table_name
select userno,count(*) from table_name where user = userno
group by user;
if it returns empty row then simply write(query 2) i.e it is a new user to be inserted:
insert into table_name values(userno,1);
I have a system that allows a person to select a form type that they want to fill out from a drop down box. From this, the rest of the fields for that particular form are shown, the user fills them out, and submits the entry.
Form Table:
| form_id | age_enabled | profession_enabled | salary_enabled | name_enabled |
This describes the metadata of a form so the system will know how to draw it. So each _enabled column is a boolean true if the form should include a field to be filled out for this column.
Entry Table:
| entry_id | form_id | age | profession | salary | name | country |
This stores a submitted form. Where age, profession, etc stores the actual value filled out in the form (or null if it didn't exist in the form)
Users can add new forms to the system on the fly.
Now the main question: I would like to add the ability for a user designing a new form to be able to include a list of possible values for an attribute (e.g. profession is a drop down list of say 20 professions instead of just a text box when filling out the form). I can't simply store a global list of possible values for each column because each form will have a different list of values to pick from.
The only solution I can come up with is to include another set of columns in Form table like profession_values and then store the values in a character delimited format. I am concerned that a column may one day have a large number of possible values and this column will get out of control.
Note that new columns can be added later to Form if necessary (and thus Entry in turn), but 90% of forms have the same base set of columns, so I think this design is better than an EAV design. Thoughts?
I have never seen a relational design for such a system (as a whole) and I can't seem to figure out a decent way to do this.
Create a new table to contain groups of values:
CREATE TABLE values (
id SERIAL,
group INT NOT NULL,
value TEXT NOT NULL,
label TEXT NOT NULL,
PRIMARY KEY (id),
UNIQUE (group, value)
);
For example:
INSERT INTO values (group, value, label) VALUES (1, 'NY', 'New York');
INSERT INTO values (group, value, label) VALUES (1, 'CA', 'California');
INSERT INTO values (group, value, label) VALUES (1, 'FL', 'Florida');
So, group 1 contains three possible values for your drop-down selector. Then, your form table can reference what group a particular column uses.
Note also that you should add fields to a form via rows, not columns. I.e., your app shouldn't be adjusting the schema when you add new forms, it should only create new rows. So, make each field its own row:
CREATE TABLE form (
id SERIAL,
name TEXT NOT NULL,
PRIMARY KEY (id)
);
CREATE TABLE form_fields (
id SERIAL,
form_id INT NOT NULL REFERENCES form(id),
field_label TEXT NOT NULL,
field_type INT NOT NULL,
field_select INT REFERENCES values(id),
PRIMARY KEY (id)
);
INSERT INTO form (name) VALUES ('new form');
$id = last_insert_id()
INSERT INTO form_fields (form_id, field_label, field_type) VALUES ($id, 'age', 'text');
INSERT INTO form_fields (form_id, field_label, field_type) VALUES ($id, 'profession', 'text');
INSERT INTO form_fields (form_id, field_label, field_type) VALUES ($id, 'salary', 'text');
INSERT INTO form_fields (form_id, field_label, field_type, field_select) VALUES ($id, 'state', 'select', 1);
I think you are starting from the wrong place entirely.
| form_id | age_enabled | profession_enabled | salary_enabled | name_enabled |
Are you just going to keep adding to this table for every single for field you can ever have? Generically the list could be endless.
How will your application code display a form if all the fields are in columns in this table?
What about a form table like this:
| form_id | form description |
Then another table, formAttributes with one row per entry on the form:
| attribute_id | form_id | position | name | type |
Then a third table forAttributeValidValues with one row per attribute valid value:
| attribute_id | value_id | value |
This may seem like more work to begin with, but it really isn't. THink about how easy it is to add or remove new attribute or value to a form. Also think about how your application will render the form:
for form_element in (select name, attribute_id
from formAttributes
where form_id = :bind
order by position asc) loop
render_form_element
if form_element.type = 'list of values' then
render_values with 'select ... from formAttributeValidValues'
end if
end loop;
The dilema will then become how to store the form results. Ideally you would store them with 1 row per form element in a table that is something like:
| completed_form_id | form_id | attribute_id | value |
If you only ever work on one form at a time, then this model will work well. If you want to do aggregations over lots of forms, then the resulting queries become more difficult, however that is reporting, which can run in a different process to the online form entry. You can start to think of things that pivot queries to transform the rows in into columns or materialized view to pull together forms of the same type etc.
I am trying to limit the about of tables in my database because I hear that's good (I am a novice web developer). I want to have the user input a list into the database. So they input a title, overall comment, and then start making the list. I can't figure out how to do this without making a new table for each list. Because, say one user wants a list with 44 values and another user wants a list of 10 values. I can't think of how to do this without making a new table for each list. I would really appreciate any help/insight you can give to me.
Basically, you want to make a table for the user lists, where each row in the table refers to one user's lists, and another table for the user list values, where each row in the table has a column for a reference to the list it belongs to, and a column for the value the user input.
Your Table Could Be:
UserID, int
ListID, int (Primary Key-Unique Identifier)
Title, VarChar(250)
Comment, VarChar(MAX)
Example Content:
1 | 1 | The Title | My Comment
1 | 2 | The Other Title | My other comment
2 | 3 | First Comment | Second Person, first comment
Eacher User just gets their list from a query:
Select ListID, Titel, Comment FROM the_Table
where UserID = #UserID
You can get away with a single table of lines for all the lists, say for example simply
CREATE TABLE ListLines (
listID INTEGER,
lineNo INTEGER,
line TEXT,
PRIMARY KEY (listID, lineNo),
FOREIGN KEY (listID) REFERENCES Lists
);
with the table of lists becoming:
CREATE TABLE Lists (
listID INTEGER PRIMARY KEY,
userID INTEGER,
title TEXT,
comment TEXT,
FOREIGN KEY (userID) REFERENCES Users
);
assuming you have a Users table with primary key userID INTEGER with per-user information (name, etc, etc).
So to get all the lines of a list given its ID you just
SELECT line FROM ListLines
WHERE listID=:whateverid
ORDER BY lineNo;
or you could UNION that with e.g. the title:
SELECT title AS line FROM Lists
WHERE listID=:whateverid
UNION ALL
SELECT line FROM ListLines
WHERE listID=:whateverid
ORDER BY lineNo;
and so on. This flexible and efficient arrangement is the relational way of doing things...