SQL - Poor design choice? - sql

Please take a look at the SQL below:
create table DatasetToID(
Dataset varchar(100),
ID INT,
Name varchar(100),
age varchar(100),
primary key (dataset,id)
)
INSERT INTO DatasetToID VALUES ('Sales', 1, 'Ian Ratkin','30')
INSERT INTO DatasetToID VALUES ('Finance', 1, 'Bert Edwards','56')
INSERT INTO DatasetToID VALUES ('Production', 1, 'Marie Edwards','56')
INSERT INTO DatasetToID VALUES ('Sales', 2, 'Karen Bromley','30')
INSERT INTO DatasetToID VALUES ('Finance', 2, 'Steven Tardy','56')
INSERT INTO DatasetToID VALUES ('Production', 2, 'Eric Bishop','56')
create table Deletion(
Dataset varchar(100),
ID INT, decision bit,
date datetime
)
INSERT INTO Deletion VALUES ('Sales', 1, 1, '2013-01-01')
INSERT INTO Deletion VALUES ('Finance', 2, 1, '2013-01-01')
INSERT INTO Deletion VALUES ('Sales', 1, 0, '2013-02-02')
A live system I support is designed like this. Records are deleted from DatasetToID and Deletion at the end of each month if the most recent Deletion decision (bit) is true. In the case of the above Finance,2 will be deleted but Sales,1 will not because Sales,1 most recent decision is 0 (false).
I believe this is quite a poor design. I believe that Dataset and ID should be in a different table like i.e. not DatasetToID. The original developer seemed to disagree before he left. I am wandering if I am wrong.

It's a denormalized design, which is common in some scenarios for this kind of work. In particular, a periodic routine like a monthly delete or archive should really not be influencing your schema design. If this is the only overlap between that key pair, then I would say your old dev was probably right. If these two columns appear together in tables, however, you are probably right, there should be a master record for this pairing.

Related

SQLite - migrating linked data, how to avoid hard-coded primary keys

I'm writing SQL migration for SQLite DB and I have linked tables, so I have to insert 'root' table row first and then to insert 'children' rows which will use ID for 'root' row.
One option could be to hard-code primary key of the 'root' row and reuse it later in all dependent rows, but I don't like to risk having 'duplicate ID' errors.
I know there's 'last_insert_rowid' in SQLite and I can use temporary table to save this value for the following inserts, but still maybe I'm missing more optimal way to do it.
For example I have tables:
Plant(id)
PlantDescription(id, language_id, plant_id, desctiption)
PlantLink(id, language_id, plant_id, link)
and I want the following inserts:
INSERT INTO Plant VALUES (123);
INSERT INTO PlantDescription VALUES (1, 1, 123, 'some description');
INSERT INTO PlantLink VALUES (1, 1, 123, 'http://eng-link.com');
Here to insert PlantDescription and PlantLink I need to know primary ID of previously inserted Plant.
I'm looking for the best practice to achieve that.
I'm developing Android app and using Android Room, but probably the question is more general and any SQLite solution should fit.

Create a time with time in SQL Server

I am very new to this please any help would be great! I am creating a date warehouse for a credit card company for school and in this table it is asking for the table TRANSACTION to having the following Date, Time, Amount, and Authorization Code. Here is what I have so far for just this table
CREATE TABLE transactions (
trans_date DATE PRIMARY KEY NOT NULL,
trans_time DATETIME NOT NULL,
trans_amount DECIMAL NOT NULL,
auth_code VARCHAR(15) NOT NULL
);
INSERT INTO transactions VALUES ('1984-12-15', '2000-03-15 11:15:23', 200.00, 'IH1546');
INSERT INTO transactions VALUES ('2001-01-22', '2014-05-15 12:45:20', 300.00, 'IH2563');
INSERT INTO transactions VALUES ('1998-10-30', '2017-01-14 13:11:45', 400.00, 'IH4457');
INSERT INTO transactions VALUES ('2003-02-11', '2007-10-28 09:05:56', 500.00, 'IH8977');
INSERT INTO transactions VALUES ('1985-12-23', '2009-06-29 16:37:03', 600.00, 'IH9975');
SELECT *
FROM transactions;
The problem I am encountering is
Msg 273, Level 16, State 1, Line 18
Cannot insert an explicit value into a timestamp column. Use INSERT with a column list to exclude the timestamp column, or insert a DEFAULT into the timestamp column.
Can anyone help me or walk me through what I need to do for time, I have been searching online for two days and not really finding anything that comes close to what I need. Each record should all have different times in them?

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)

SQL - Data in two places

Please have a look at the DDL below:
CREATE TABLE Sport (ID int, Description varchar(50),primary key(ID))
CREATE TABLE Audit (AditID int, PersonID int, SportID int, AuditDate datetime,primary key(id))
CREATE Table Person (ID int not null, Name varchar(50), FavouriteSportID int, PRIMARY KEY (id),
FOREIGN KEY (FavouriteSportID) REFERENCES Sport(Id))
INSERT INTO Sport VALUES (1,'Football')
INSERT INTO Sport VALUES (2,'Basketball')
INSERT INTO Sport VALUES (3,'Squash')
INSERT INTO Person VALUES (1,'Ian',1)
INSERT INTO Audit VALUES (1,1,1,'2012-01-01')
INSERT INTO Audit VALUES (2,1,1,'2012-02-01')
INSERT INTO Audit VALUES (3,1,'2012-03-01')
The Audit table shows the Persons favorite sport in the past.
I am conscious that this involves storing the favorite sport in two places i.e. Person.FavouriteSportID and the most recent audit record i.e. audit record ID 3 also shows that Ian's current favorite sport is Squash (because it has the most recent audit record).
I am wondering if there is a better design for this simple requirement.
It's fine. It's good, in fact.
Alternatively, you could use AuditId in place of SportId in Person, as a reference to the latest audit row with the current SportId, but that makes Audit an active part of the current representation. I like your design better, since the current representation is confined to fewer tables, and the audit table is an outrigger. It's also much easier to automate--a simple after update, delete trigger on Person will reliably maintain the audit table with no further effort from anybody.
EDIT
If you don't need to record the current association in the audit table, then don't, and you haven't stored the same info twice. As mentioned, an after update, delete trigger will do nicely
-- syntax assuming SQL Server 2008+, adapt as appropriate
create trigger tr_Person_Audit on Person
after update, delete
as begin
insert Audit (PersonId, SportId, ExpirationDate)
select PersonId, SportId, getdate() -- as expiration date
from (
select PersonId, SportId from deleted except
select PersonId, SportId from inserted -- control for updates where nothing changed
) this
end
If you do need to record the current association in the audit table, it's not quite as clean, but Audit is still an outrigger and you still can automate it with an after insert, update trigger:
-- syntax assuming SQL Server 2008+, adapt as appropriate
create trigger tr_Person_Audit on Person
after insert, update
as begin
insert Audit (PersonId, SportId, EffectiveDate)
select PersonId, SportId, getdate() -- as effective date
from (
select PersonId, SportId from inserted except
select PersonId, SportId from deleted -- control for updates where nothing changed
) this
end
You could also drop the column Person.FavouriteSportID and query the most recent audit when you need it. It's easier to keep the data consistent.
SELECT TOP 1 SportID
FROM Audit
WHERE PersonID = #1
ORDER BY AuditDate DESC
Or
SELECT
Person.*,
(SELECT TOP 1 Audit.SportID
FROM Audit
WHERE Audit.PersonID = Person.PersonID
ORDER BY Audit.AuditDate DESC) AS FavouriteSportID
FROM
Person

SQL Server Insert Example

I switch between Oracle and SQL Server occasionally, and often forget how to do some of the most trivial tasks in SQL Server. I want to manually insert a row of data into a SQL Server database table using SQL. What is the easiest way to do that?
For example, if I have a USERS table, with the columns of ID (number), FIRST_NAME, and LAST_NAME, what query do I use to insert a row into that table?
Also, what syntax do I use if I want to insert multiple rows at a time?
To insert a single row of data:
INSERT INTO USERS
VALUES (1, 'Mike', 'Jones');
To do an insert on specific columns (as opposed to all of them) you must specify the columns you want to update.
INSERT INTO USERS (FIRST_NAME, LAST_NAME)
VALUES ('Stephen', 'Jiang');
To insert multiple rows of data in SQL Server 2008 or later:
INSERT INTO USERS VALUES
(2, 'Michael', 'Blythe'),
(3, 'Linda', 'Mitchell'),
(4, 'Jillian', 'Carson'),
(5, 'Garrett', 'Vargas');
To insert multiple rows of data in earlier versions of SQL Server, use "UNION ALL" like so:
INSERT INTO USERS (FIRST_NAME, LAST_NAME)
SELECT 'James', 'Bond' UNION ALL
SELECT 'Miss', 'Moneypenny' UNION ALL
SELECT 'Raoul', 'Silva'
Note, the "INTO" keyword is optional in INSERT queries. Source and more advanced querying can be found here.
Here are 4 ways to insert data into a table.
Simple insertion when the table column sequence is known.
INSERT INTO Table1 VALUES (1,2,...)
Simple insertion into specified columns of the table.
INSERT INTO Table1(col2,col4) VALUES (1,2)
Bulk insertion when...
You wish to insert every column of Table2 into Table1
You know the column sequence of Table2
You are certain that the column sequence of Table2 won't change while this statement is being used (perhaps you the statement will only be used once).
INSERT INTO Table1 {Column sequence} SELECT * FROM Table2
Bulk insertion of selected data into specified columns of Table2.
.
INSERT INTO Table1 (Column1,Column2 ....)
SELECT Column1,Column2...
FROM Table2
I hope this will help you
Create table :
create table users (id int,first_name varchar(10),last_name varchar(10));
Insert values into the table :
insert into users (id,first_name,last_name) values(1,'Abhishek','Anand');
For example, "person" table has "id" IDENTITY column as shown below:
CREATE TABLE person (
id INT IDENTITY, -- Here
name NVARCHAR(50),
age INT,
PRIMARY KEY(id)
)
Then, we don't need to manually put a value to "id" IDENTITY column when inserting a row:
INSERT INTO person VALUES ('John', 27) -- The value for "id" is not needed
And, we can also insert multiple rows without the values for "id" IDENTITY column:
INSERT INTO person VALUES ('John', 27), ('Tom', 18)