Connecting Three SQL Server Tables Together to Display Images - c#

I am trying to create a Photo Album/Collection for each student in my ASP.NET website, using SQL Server tables.
Here are my table structure:
I want to store the image name within the Student_Images folder. Then each image will be linked to a Photo Collection. Each Photo Collection is linked to a group.
Then I want to display each group, any photo collections which are associated with that group, and then any images associated with that collection.
I have been able to display images related to each student, but I don't know how to add the 'Photo Collection' table between the Students and Images tables.
Here is how I would like it to display:
Here is how the tables would look populated:
Can anyone advise me as to how to go about making this connection?

There's an idea of a foreign key - where a row in a table refers to a row in another table. You definitely want to do this for a number of reasons, to ensure data integrity and to document your relationships for whoever works on the site.
You don't quite have enough information to get where you want to get. You don't have any linkage between photo albums and the images they contain. You'll need another table for that.
I kinda recommend that you use column names that don't repeat the table name. You'll find you can then see patterns more clearly in your schema. For example Photo_Collection.Photo_Collection_Id should probably be just Photo_Collection.Id. It's more concise, and it's obvious which Id you're talking about because you always use the table name when referring to that column.
So, to get you nearer to where you need to be, I'd recommend something like this:
create table dbo.Students
(
ID
int not null identity( 1, 1 )
constraint [Students.ID.PrimaryKey]
primary key clustered,
Name
nvarchar( 50 ) --> not very generous :-)
)
go
create index [Students.Name.Index] on dbo.Students( Name ) --> maybe unique?
go
create table dbo.Student_Images
(
ID
int not null identity( 1, 1 )
constraint [Student_Images.ID.PrimaryKey]
primary key clustered,
Student_ID
int not null
constraint [Student_Images.to.Student]
foreign key references dbo.Students( ID )
Filename
nvarchar( 250 ) null, --> null? really? each image should have a unique file name, dont you think?
Description
nvarchar( 250 ) null
)
go
create index [Student_Images.to.Students.Index] on dbo.Student_Images( Student_ID )
go
create table dbo.Photo_Collection
(
ID
int not null identity( 1, 1 )
constraint [Photo_Collection.ID.PrimaryKey]
primary key clustered,
Name
nvarchar( 250 ) null --> null? hmmm...could be hard to use
)
go
create index [Photo_Collection.Name.Index] on dbo.Photo_Collection( Name ) --> consider unique??
go
create table dbo.Photo_Collection_Images
(
Photo_Collection_ID
int not null
constraint [Photo_Collection_Images.to.Photo_Collection]
foreign key references dbo.Photo_Collection( ID ),
Student_Image_ID
int not null
constraint [Photo_Collection_Images.to.Student_Images]
foreign key references dbo.Student_Images( ID )
)
You didn't really describe your groups...and there are lots of questions still unanswered. For example, are Photo_Collections something that a student makes? If so, there should probably be a Student_ID in Photo_Collection with a foreign key to Students.
The table I added called Photo_Collection_Images relates your photo collections to the images. You'd include this table in any query that needs to display all the images in a given photo collection. I think that's the main missing bit for you. You'd do something similar for groups.
Also - FYI, pasting images of your text is kinda aggravating. Consider just pasting the text in and indenting it 4 spaces to get it to format correctly.
Edit:
To select, for example, all the images in a photo collection by student, you could do:
select
pc.Name PhotoCollectionName,
si.FileName FileName,
si.Description FileDescription,
s.Name StudentName
from
dbo.Photo_Collection pc
inner join
dbo.Photo_Collection_Images pci
on
pc.Id = pci.Photo_Collection_ID
inner join
dbo.Student_Image si
on
pci.Student_Image_ID = si.ID
inner join
dbo.Students s
on
si.Student_Id = s.ID
This is pretty much the base query for everything.

Make some changes in your Photo_Collection table, create an FK within your Photo_Collection of Student_ID, then you'll be able to show the photo collection of all the students because through Student_ID, you can have access to student table as well as Student_Images.
Hope it helps.

Related

How to Auto-increment non-integer primary key in sql-server? [duplicate]

Can I make a primary key like 'c0001, c0002' and for supplier 's0001, s0002' in one table?
The idea in database design, is to keep each data element separate. And each element has its own datatype, constraints and rules. That c0002 is not one field, but two. Same with XXXnnn or whatever. It is incorrect , and it will severely limit your ability to use the data, and use database features and facilities.
Break it up into two discrete data items:
column_1 CHAR(1)
column_2 INTEGER
Then set AUTOINCREMENT on column_2
And yes, your Primary Key can be (column_1, column_2), so you have not lost whatever meaning c0002 has for you.
Never place suppliers and customers (whatever "c" and "s" means) in the same table. If you do that, you will not have a database table, you will have a flat file. And various problems and limitations consequent to that.
That means, Normalise the data. You will end up with:
one table for Person or Organisation containing the common data (Name, Address...)
one table for Customer containing customer-specific data (CreditLimit...)
one table for Supplier containing supplier-specific data (PaymentTerms...)
no ambiguous or optional columns, therefore no Nulls
no limitations on use or SQL functions
.
And when you need to add columns, you do it only where it is required, without affecting all the other sues of the flat file. The scope of effect is limited to the scope of change.
My approach would be:
create an ID INT IDENTITY column and use that as your primary key (it's unique, narrow, static - perfect)
if you really need an ID with a letter or something, create a computed column based on that ID INT IDENTITY
Try something like this:
CREATE TABLE dbo.Demo(ID INT IDENTITY PRIMARY KEY,
IDwithChar AS 'C' + RIGHT('000000' + CAST(ID AS VARCHAR(10)), 6) PERSISTED
)
This table would contain ID values from 1, 2, 3, 4........ and the IDwithChar would be something like C000001, C000002, ....., C000042 and so forth.
With this, you have the best of both worlds:
a proper, perfectly suited primary key (and clustering key) on your table, ideally suited to be referenced from other tables
your character-based ID, properly defined, computed, always up to date.....
Yes, Actually these are two different questions,
1. Can we use varchar column as an auto increment column with unique values like roll numbers in a class
ANS: Yes, You can get it right by using below piece of code without specifying the value of ID and P_ID,
CREATE TABLE dbo.TestDemo
(ID INT IDENTITY(786,1) NOT NULL PRIMARY KEY CLUSTERED,
P_ID AS 'LFQ' + RIGHT('00000' + CAST(ID AS VARCHAR(5)), 5) PERSISTED,
Name varchar(50),
PhoneNumber varchar(50)
)
Two different increments in the same column,
ANS: No, you can't use this in one table.
I prefer artificial primary keys. Your requirements can also be implemented as unique index on a computed column:
CREATE TABLE [dbo].[AutoInc](
[ID] [int] IDENTITY(1,1) NOT NULL,
[Range] [varchar](50) NOT NULL,
[Descriptor] AS ([range]+CONVERT([varchar],[id],(0))) PERSISTED,
CONSTRAINT [PK_AutoInc] PRIMARY KEY ([ID] ASC)
)
GO
CREATE UNIQUE INDEX [UK_AutoInc] ON [dbo].[AutoInc]
(
[Descriptor] ASC
)
GO
Assigning domain meaning to the primary key is a practice that goes way, way back to the time when Cobol programmers and dinosaurs walked the earth together. The practice survives to this day most often in legacy inventory systems. It is mainly a way of eliminating one or more columns of data and embedding the data from the eliminated column(s) in the PK value.
If you want to store customer and supplier in the same table, just do it, and use an autoincrementing integer PK and add a column called ContactType or something similar, which can contain the values 'S' and 'C' or whatever. You do not need a composite primary key.
You can always concatenate these columns (PK and ContactType) on reports, e.g. C12345, S20000, (casting the integer to string) if you want to eliminate the column in order to save space (i.e. on the printed or displayed page), and everyone in your organization understands the convention that the first character of the entity id stands for the ContactType code.
This approach will leverage autoincrementing capabilities that are built into the database engine, simplify your PK and related code in the data layer, and make your program and database more robust.
First let us state that you can't do directly. If you try
create table dbo.t1 (
id varchar(10) identity,
);
the error message tells you which data types are supported directly.
Msg 2749, Level 16, State 2, Line 1
Die 'id'-Identitätsspalte muss vom
Datentyp 'int', 'bigint', 'smallint',
'tinyint' oder 'decimal' bzw.
'numeric' mit 0 Dezimalstellen sein
und darf keine NULL-Werte zulassen.
BTW: I tried to find this information in BOL or on MSDN and failed.
Now knowing that you can't do it the direct way, it is a good choice to follow #marc_s proposal using computed columns.
Instead of doing 'c0001, c0002' for customers and 's0001, s0002' for suppliers in one table, proceed in the following way:
Create one Auto-Increment field "id" of Data Type "int (10) unsigned".
Create another field "type" of Data Type "enum ('c', 's')" (where c=Customer, s=Supplier).
As "#PerformanceDBA" pointed out, you can then make the Primary Key Index for two fields "id" & "type", so that your requirement gets fulfilled with the correct methodology.
INSERT INTO Yourtable (yourvarcharID)
values('yourvarcharPrefix'+(
SELECT CAST((SELECT CAST((
SELECT Substring((
SELECT MAX(yourvarcharID) FROM [Yourtable ]),3,6)) AS int)+1)
AS VARCHAR(20))))
Here varchar column is prefixed with 'RX' then followed by 001, So I selected substring after that prefix of it and incremented the that number alone.
We can add Default Constraint Function with table definition to achieve this.
First create table -
create table temp_so (prikey varchar(100) primary key, name varchar(100))
go
Second create new User Defined Function -
create function dbo.fn_AutoIncrementPriKey_so ()
returns varchar(100)
as
begin
declare #prikey varchar(100)
set #prikey = (select top (1) left(prikey,2) + cast(cast(stuff(prikey,1,2,'') as int)+1 as varchar(100)) from temp_so order by prikey desc)
return isnull(#prikey, 'SB3000')
end
go
Third alter table definition to add default constraint -
alter table temp_so
add constraint df_temp_prikey
default dbo.[fn_AutoIncrementPriKey_so]() for prikey
go
Fourth insert new row into table without specifying value for primary column-
insert into temp_so (name) values ('Rohit')
go 4
Check out data in table now -
select * from temp_so
OUTPUT -
prikey name
SB3000 Rohit
SB3001 Rohit
SB3002 Rohit
SB3003 Rohit
you may try below code:
SET #variable1 = SUBSTR((SELECT id FROM user WHERE id = (SELECT MAX(id) FROM user)), 5, 7)+1;
SET #variable2 = CONCAT("LHPL", #variable1);
INSERT INTO `user`(`id`, `name`) VALUES (#variable2,"Jeet");
1st line to get last inserted Id by removing four character than increase one value and set to a variable1
2nd line to make complete id with four character prefix and assign to variable2
insert new value with generated new primary key = variable2
you should have minimum one data in this table to work above SQL
No. If you really need this, you will have to generate ID manually.

Get foreign key field from primary key in another table that linked to many table

My database is designed in SQL Server & I want get output in asp.net, LINQ, C#
I have 2 tables linked to 1 table (1:1)
My question is can I get a primary key linked to which table?
For example:
tbl_Document (ID, Date, ...)
tbl_Factor (ID, DocID, ...)
tbl_Finance (ID, DocID, ...)
What is the best way to know ID in tbl_Document linked to which table?
I can add record in tbl_Document as 'whichTable' and write the name of table in every column, and every time I want to search set "if" and check 'WhichTable'.
Is there a better way to do that?
Thanks, and sorry for my bad English :)
By default, you can get only all tables that have foreign key constraints to the parent table:
select object_name(f.referenced_object_id) pk_table, c1.name pk_column_name,
object_name(f.parent_object_id) fk_table, c2.name fk_column_name
from
sys.foreign_keys f
join sys.columns c1 on c1.object_id = f.referenced_object_id
join sys.columns c2 on c2.object_id = f.parent_object_id
join sys.foreign_key_columns k
on (k.constraint_object_id = f.object_id
and c2.column_id = k.parent_column_id
and c1.column_id = k.referenced_column_id )
where object_name(f.referenced_object_id) ='tbl_Document'
There is no such additional information regarding every particular row in parent table. It would be a duplicate information (since you can figure out it by searching in every child table). Thus, as you mentioned you can store child table name in the additional column and then as an option, construct sql dynamically to query child row.

How would I create "multiple choice" columns in Local Database?

I am using local database for first time with my WPF project. I have the database setup, and I am connecting fine ETC. Ther eare some columns which I want to be multiple choice, either between a few values or a whole bunch of values. Problem is obviously human error will make typos now and then when inputting the data.
How would I go about making the data entry give the user a multiple choice? So for example, I have a column called "Category", and at the moment (this will be expanded later) I only want to allow the following options:
Bronze
Misc
I have the columns set to nvarchar(50) at present, but typing the same string manually constantly... not what I would like to be doing TBH... so... Could I set it so that there are a list of predefined values it will accept? :)
thanks :D
You can use CHECK constraint of any complexity on your table column(s). Check MSDN here
So your table definition would be as:
CREATE TABLE T
(
Category nvarchar(50) CHECK (Category in ('Bronze','Misc'))
)
If you expect your list of possible values to change in the future and you do not want to change a table definition, you can create a separate table with the list of values and use the foreign key.
CREATE TABLE Categories
(
Id int PRIMARY KEY,
CategoryName nvarchar(50)
)
INSERT INTO Categories VALUES (1, 'Bronze'), (2, 'Silver'), (3, 'Misc')
CREATE TABLE T
(
CategoryId int REFERENCES Categories
)

Database design for compiling records from another table

What's the best way to design a table that reference multiple records from another table?
For example, there is a table called diary that stores subjects, descriptions and keywords, then another table called DiaryCompilation for combining all selected records into a book by just referencing the id from the diary.
What's the best way to create the DiaryCompilation?
I was thinking of consisting it into two fields: id, references
where in all selected records are placed in references, but is it a good practice or are there better approaches?
<--- Each record is a new entry of diary --->
Diary: ID, Subject, Description, Keywords
<--- Single record per compilation for summary info --->
DiaryCompilation: ID, Title
<--- Pages of Diary Compilation --->
DiaryCompilationEntries: DiaryCompilationID, DiaryID
Diary and DiaryCompilation would have a 1 to many relationship. Just make sure that the Id in the DairyCompilation is setup as the Primary Key and put a Foreign Key constraint on it so that it ties back to an Id in the Diary table. This will prevent you from deleting a diary and orphaning a record in the DiaryCompilation table. As long as you have a normalized data model you should be fine.
Keep your fields atomic. Placing several values in references field would make it much harder to query and to enforce referential constraints.
A 1:N relationship between parent and child is modeled by migrating parent's primary key into child. In your case, this would look something like this:
COMPILATION (
COMPILATION_ID PK
-- Other fields...
)
DIARY (
DIARY_ID PK
COMPILATION_ID FK(COMPILATION)
SUBJECT
DESCRIPTION
)
-- Not a good idea to have several keywords in a single field, so we need a separate table for keywords.
DIARY_KEYWORD (
DIARY_ID PK, FK(DIARY)
KEYWORD PK
)
If you actually want N:N relationship (i.e. diary can be part of more than one compilation), you'll need a dedicated table to hold these connections, something like this:
COMPILATION (
COMPILATION_ID PK
)
DIARY_IN_COMPILATION (
COMPILATION_ID PK, FK(COMPILATION)
DIARY_ID PK, FK(DIARY)
)
DIARY (
DIARY_ID PK
SUBJECT
DESCRIPTION
)
DIARY_KEYWORD (
DIARY_ID PK, FK(DIARY)
KEYWORD PK
)
What's the best way to create the DiaryCompilation?
Sounds like the best way might be to use a view instead of a table.

confusion over relationships

Hey guys I have a confusing question, I have a user table and it stores all the usual data for a user that you would expect but im trying to figure out how a user could add another user?
Sounds strange but each user in the User table has his own UI which is UserID how could I add another table where UserID can have a relationship with another UserID?
I will give the answer to those that could take the time to upload a table diagram and who can help with some examples for sqlsyntax related to this question, i.e how would I right the syntax for the above question if I wanted to display all the UserIDs friends on a page. How would I add a friend.
You just need another table similar to the Pictures and wallposting table. You just need to be able to record many friend id's for one user id (one to many)
eg:
The query to then get friends would be:
DECLARE #UserID AS BIGINT
SET #UserID = 123
SELECT [friends].*
FROM [friends]
where [parentuserid]=#UserID#
to Insert a Friend:
DECLARE #UserID AS BIGINT
DECLARE #FriendID AS BIGINT
SET #UserID = 123
SET #FriendID = 321
INSERT INTO [Friends]
(
[ParentUserID],
[ChildUserID]
)
VALUES
(
#UserID,
#FriendID
)
Code to insert it example:
private void Test()
{
string Query =
#"INSERT INTO [Friends]
(
[ParentUserID],
[ChildUserID]
)
VALUES
(
#UserID,
#FriendID
)";
using ( SqlConnection oSqlConnection = new SqlConnection( "connect string" ) )
{
oSqlConnection.Open();
using (SqlCommand oSqlCommand = new SqlCommand(Query,oSqlConnection))
{
oSqlCommand.Parameters.AddWithValue("#UserID", Session["UserID"]);
oSqlCommand.Parameters.AddWithValue("#FriendID", Session["FriendID"]);
oSqlCommand.ExecuteNonQuery();
}
}
}
Where would you get your friend ID from? what is the process for adding friends? do you search for them and select them from a list?
You just need a many to many relationship.
Create Table Friend(
UserId int,
FriendId int,
Constraint pk_friends Primary Key (UserId, FriendId),
Constraint fk_friend_user Foreign Key (UserId) References User(UserId),
Constraint fk_friend_friend Foreign Key (FriendId) References User(UserId)
)
Your asking a bit much with the diagrams etc.... for me at least.
It depends on the kind of relationship, one to one, it will be another Column in your user table. If it is many to many you'll need a junction table, with two columns UserIDA, UserIDB. Depending why you need the relationship.
I think you have a simple requirement of a User Referring Other user to join. If that is the case then you can simply have one more column ReferenceUserID in User Table and whenever a new user refers another user then simply add him with a UserID of referring user. If he isn't a referred user then by default it will be NULL.
Later for retrieval you can use a Self Join.
Update:
For Friend (many to many relationship) you should look at following Question at StackOverflow
Database design: Best table structure for capturing the User/Friend relationship?
UserRelationship
====
RelatingUserID
RelatedUserID
Type[friend, block, etc]
A similar approach is used by Facebook where they have a kind of Cross join Table for relation between Friends.

Categories