I have a denormalized table that contains up to 8 user photos:
CREATE TABLE [dbo].[Users] (
Id int NOT NULL,
ProfileId int NOT NULL,
Photo1 int DEFAULT NULL,
Photo2 int DEFAULT NULL,
Photo3 int DEFAULT NULL,
Photo4 int DEFAULT NULL,
Photo5 int DEFAULT NULL,
Photo6 int DEFAULT NULL,
Photo7 int DEFAULT NULL,
Photo8 int DEFAULT NULL,
CONSTRAINT [PK_Users_ProfileId_Id] PRIMARY KEY CLUSTERED ([ProfileId], [Id])
)
A user can change the position of each photo (move it). What I would like to achieve is that when user moves a photo to another position, this photo must be saved to a different Photo-column and the rest of photos must be reordered:
If I move Photo8 to Photo1, it should save Photo8 to Photo1, Photo1 to Photo2, Photo2 to Photo3 ...
And if I move Photo2 to Photo4, it should save Photo2 to Photo4, Photo3 to Photo2, Photo4 to Photo3.
How can I achieve this with SQL and preferably without dynamic-generated SQL (EXEC-Method).
You should consider changing your DB design. For instance:
users table
-----------
id int
profile_id int
name varchar
....
photos table
------------
id int
user_id int
rank int
Then a user can have as many photos as he/she likes and you only need to change the rank to change the order of the photos.
This would then set photo 8 to 1 and the others will be reordered:
update photos
set rank = case when rank = 8 then 1
when rank between 1 and 7 then rank + 1
else rank
end
where user_id = 1
A thunb rule for DB design is when you need numbers in your column names (like photo1, photo2) then it is bad.
I'm assuming you do not want to change the DB. Consider solving this in the application code like this:
Copy the PhotoIDs to a List (with up to 8 items obviously)
Insert/delete/reorder as you like (this is easy in most languages)
Copy the list contents back to the columns
Steps 1 and 3 are reusable so you only need to write them once.
Related
Players{
Pid int primary key,
tid int not null references Teams,
name text not null,
age int not null
}
Teams{
tid int primary key,
name text not null,
location not null
}
Possessions{
id int primary key,
pid int not null references Players,
time int not null, //the time the possession started for a player
held int not null //for how much time he had the ball
}
I would like to create a view called Teampasses where I can select (passer,passee) as follows:Passer and passe must be from the same team and passes possession starting time equals to passes possession starting time +held (time he has the ball). What I have done so far is this:
SELECT x.name AS passer,y.name as Pasee
FROM player x
INNER JOIN player y ON x.tid=y.tid
INNER JOIN possesions p ON p.pid=x.pid AND p.pid=y.pid AND ...
in the ... section right of AND i would like to do something like x.time+x.held=y.time.How could i refer to there two?
I see an issue with your data:
Your Possessions table only has a single foreign key to the Players table for the passer. It needs to also include the Pid of the Passee. Otherwise, there's no way to filter out which player on the passer team is the Passee for a given Possession.
I would suggest changing the Possessions table as follows:
Possessions(
id int primary key,
pid_passer int not null references Players,
pid_passee int not null references Players,
time int not null, //the time the possession started for a player
held int not null //for how much time he had the ball
)
With this change, your data will work and the query becomes trivial.
I am trying to add a permission hierarchy for users e.g. child, parent, admin
My idea is to create a generic user table with usernames and passwords and accessibility which would be from 0 to 2 , 0 - child 1 - parent 2 - admin, but I do not know how to connect the user table to parent/child table since they have different variables.
Access picture of my database right now
To be clear parent/child wouldn't have username/password like in this picture anymore with user table.
Update:
DB using only one table for users
This table would keep the fields that are only for parents empty if this is a child etc. I would want feedback if the variable 'accessibility' makes sense that would be value from 0 to 2 which would allow me in code to check if it is a parent child or admin
What you want is a kind-of table inheritance that prevents two derived entities from sharing the same supertype instance.
Databases like Access and MS SQL Server do not support table-inheritance like how PostgreSQL does, but you can fake it:
TABLE Users (
UserId int PRIMARY KEY IDENTITY(1,1),
UserName nvarchar(50) NOT NULL,
PasswordHash binary(32) NOT NULL, -- assuming sha256
PasswordSalt binary(8) NOT NULL
)
TABLE Parents (
UserId int NOT NULL PRIMARY KEY,
-- insert parent-specific fields here
FOREIGN KEY UserId REFERENCES Users ( UserId )
)
TABLE Children (
UserId int NOT NULL PRIMARY KEY,
-- insert child-specific fields here
FOREIGN KEY UserId REFERENCES Users ( UserId )
)
This schema means that a Parent or Children entity cannot exist without a single specific User entity also existing. However because Access does not support true table-inheritance it cannot easily constrain UserId values such that only 1 Parent or Children row can have that value (i.e. there can be a Parent AND a Child that share the same UserId value).
Fortunately there's a hack that is mathematically correct (as far as relational-algebra is concerned) but which unfortunately breaks aspects of OOP in that the superclass (User) is now aware of it subclasses - but this might be desirable is certain circumstances...
...anyway. the trick is to add an enum value to User's primary key (so it's a composite-key) which identifies a singular subclass, then add a constant (enforced via CHECK CONSTRAINT) composite-key component to match in each "derived" table, like so (using pseudo-SQL - the relational-algebra is portable, but concepts like enums and check-constraints don't necessarily port to MS Access very well):
ENUM UserType ( HumanParent = 1, HumanChild = 2, Other = 3 )
TABLE Users (
UserId int IDENTITY(1,1),
UserType UserType NOT NULL,
UserName nvarchar(50) NOT NULL,
PasswordHash binary(32) NOT NULL, -- assuming sha256
PasswordSalt binary(8) NOT NULL
PRIMARY KEY ( UserId, UserType )
)
TABLE Parents (
UserId int NOT NULL,
UserType UserType NOT NULL,
-- insert parent-specific fields here
PRIMARY KEY ( UserId, UserType )
FOREIGN KEY ( UserId, UserType ) REFERENCES Users ( UserId, UserType )
CHECK CONSTRAINT UserType = 1
)
TABLE Children (
UserId int NOT NULL,
UserType UserType NOT NULL,
-- insert child-specific fields here
PRIMARY KEY ( UserId, UserType )
FOREIGN KEY ( UserId, UserType ) REFERENCES Users ( UserId, UserType )
CHECK CONSTRAINT UserType = 2
)
So at the cost of slight inefficiency (i.e. the extra storage needed for the UserType columns - and computational expense of evaluating the CHECK constraints) you gain guarantees of the correctness of your data.
Now have fun porting that to Access :)
I have the following table DDL:
CREATE TABLE [dbo].[Test] (
[UserId] NVARCHAR (128) NOT NULL,
[TestId] INT IDENTITY (1, 1) NOT NULL,
[ExamId] INT NOT NULL,
[Title] NVARCHAR (100) NULL,
[Status] INT NOT NULL,
[CurrentQuestion] INT DEFAULT ((999)) NOT NULL,
CONSTRAINT [PK_Test] PRIMARY KEY CLUSTERED ([TestId] ASC)
);
This table contains:
A list of ALL Exams and Tests that are available. These rows have a UserId = 0
A list of Exam and Test that are in progress for a particular user (UserId != 0)
A user may have one or may have multiple rows in this table.
Is there a way that I can create a view that shows a list of all the tests and where it exists the additional information for a user if that user has started a test. I'm really not sure if this needs to be a view or a stored procedure as it would need to take as input a UserId.
For the UserId = 1 who in this example has started two of the available tests:
ExamId TestId Title Status
1 1 Exam1Test1 Started
1 2 Exam1Test2
2 3 Exam1Test3 Started
2 4 Exam1Test4
or for the UserId = 2 who in this example has started three of the available tests:
1 1 Exam1Test1 Started
1 2 Exam1Test2
2 3 Exam1Test3 Started
2 4 Exam1Test4 Started
Your schema implies that any given test may be taken by at most one user at a time.
The query that will return the results you want is:
select
ExamId,
TestId,
Title,
case when userId = ? then "Started" else "" end as Status
from Tests
Of course you would replace the ? in the query with the actual user you're interested in.
I have the following SQL I trigger in a C# app.
All works well but the ID table doesn't auto increment. It creates the value of 1 for the first entry then will not allow other inserts due to not being able to create a unquie ID.
Here is the SQL:
CREATE TABLE of_mapplist_raw (
id integer PRIMARY KEY NOT NULL,
form_name varchar(200) NOT NULL,
form_revi varchar(200) NOT NULL,
source_map varchar(200),
page_num varchar(200) NOT NULL,
fid varchar(200) NOT NULL,
fdesc varchar(200) NOT NULL
)";
I'm sure its a schoolboy error at play here.
you need to specify its seed and increment.( plus , i dont think there is integer keyword ....)
id [int] IDENTITY(1,1) NOT NULL,
the first value is the seed
the second one is the delta between increases
A Question you might ask :
delta between increases ? why do i need that ? its always 1 ....??
well - yes and no. sometimes you want to leave a gap between rows - so you can later insert rows between... specially if its clustered index by that key....and speed is important... so you can pre-design it to leave gaps.
p.s. ill be glad to hear other scenarios from watchers.
You need to mention the Identity.
id int IDENTITY(1,1) NOT NULL
I have something like this:
create table account
(
id int identity(1,1) primary key,
usertype char(1) check(usertype in ('a', 'b')) not null,
unique(id, usertype)
)
create table auser
(
id int primary key,
usertype char(1) check(usertype = 'a') not null,
foreign key (id, usertype) references account(id, usertype)
)
create table buser
(
... same just with b
)
the question is: if I'm going to use int instead of char(1), does it going to work faster/better ?
it doesn't matter on most modern databases. int is fine.
char as well.
(when the database fetch data from a table, it's not in byte size....)
why would you need IDENTITY columns: "auser.id" and "buser.id" that foreign key back to the "account.id" identity column?? seems hard to make sure everything could ever be in sync? When you insert into account you get an ID (say 1) and type "a", when you insert into "auser" you get an id (1) and FK to "account" how would you insert into "buser" (and get id 1) and fk back to account with 1,b??
Onto the real question. Size UserType to how many values you will have, if you will only have a few CHAR(1) is best, your index will take up less memory. if you will have more than a char(1) can hold, go tiny int (0-255, 1 byte), if you need more, go smallint (32k, 2 byte), if you need more go int (2,147,483,647, 4 byte)
A char(1) is 1 byte, whereas an int is 4 bytes. Even a small int is 2 bytes.