Create combination sql table - sql

I'm trying to create a sql table in data base in VS that has room and userid column, but the sql will only accept your input if the userid exists in users table and room exists in rooms tables
Allows:
Users table:
Userid
1
2
3
RoomUsers table:
Room ----- User
1 1
2. 1
1. 2
1. 3
2. 3
Won't allow:
Users table:
Userid
1
2
RoomUsers table:
Room ----- User
1 4
Normal foreign key wont work because it only allows one of each index and not multiple, how can I allow what I need to occur,to happen?

(This would be a mess in comments)
Probably we are having an XY problem here. The thing you describe is simply solved with a foreign key. ie:
CREATE TABLE users (id INT IDENTITY NOT NULL PRIMARY KEY, ad VARCHAR(100));
CREATE TABLE rooms (id INT IDENTITY NOT NULL PRIMARY KEY, ad VARCHAR(100));
CREATE TABLE room_user
(
RoomId INT NOT NULL
, UserId INT NOT NULL
, CONSTRAINT PK_roomuser
PRIMARY KEY(RoomId, UserId)
, CONSTRAINT fk_room
FOREIGN KEY(RoomId)
REFERENCES dbo.rooms(id)
, CONSTRAINT fk_user
FOREIGN KEY(UserId)
REFERENCES dbo.users(id)
);
INSERT INTO dbo.users(ad)
OUTPUT
Inserted.id, Inserted.ad
VALUES('RayBoy')
, ('John')
, ('Frank');
INSERT INTO dbo.rooms(ad)
OUTPUT
Inserted.id, Inserted.ad
VALUES('Room1')
, ('Room2')
, ('Room3');
INSERT INTO dbo.room_user(RoomId, UserId)VALUES(1, 1), (1, 2), (2, 3);
-- won't allow
INSERT INTO dbo.room_user(RoomId, UserId)VALUES(999, 888);

Related

Dynamically created Tables JOIN in SQL Server

I have 3 types of tables
Major table as follows
CREATE TABLE #InitialTable
(
Id int PRIMARY KEY IDENTITY(1,1),
RP varchar(20)
)
INSERT INTO #InitialTable
VALUES ('R1', 'R2', 'R3')
GO
Table contains dynamically created tables information as follows
CREATE TABLE #DynamicTablesInfo
(
Id int PRIMARY KEY IDENTITY(1,1),
RPId int FOREIGN KEY REFERENCES #InitialTable(Id),
TableName varchar(100)
)
GO
INSERT INTO #DynamicTablesInfo
VALUES (1, 'Table_X1'), (2, 'Table_X2'), (3, 'Table_X3')
GO
Dynamically created tables these tables can be any number of tables and the tables info is available in above table.
CREATE TABLE #Table_X1
(
Id int PRIMARY KEY IDENTITY,
Version_Value varchar(100)
)
GO
INSERT INTO #Table_X1
VALUES ('Val_X1_1'), ('Val_X1_2'), ('Val_X1_3')
GO
CREATE TABLE #Table_X2
(
Id int PRIMARY KEY IDENTITY,
Version_Value varchar(100)
)
GO
INSERT INTO #Table_X2
VALUES ('Val_X2_1'), ('Val_X2_2'), ('Val_X2_3')
GO
CREATE TABLE #Table_X3
(
Id int PRIMARY KEY IDENTITY,
Version_Value varchar(100)
)
GO
INSERT INTO #Table_X3
VALUES ('Val_X3_1'), ('Val_X3_2'), ('Val_X3_3')
GO
Now I wanted to join InitialTable with dynamically created tables (Table_X1, Table_X2, Table_X3,....) with the help of DynamicTablesInfo table - how to do that?
Note: for easy update, delete, insert I created them as temporary tables but in my application all are real tables.
Instead of doing this:
CREATE TABLE #Table_X1
(
Id int PRIMARY KEY IDENTITY
,Version_Value varchar(100)
)
GO
CREATE TABLE #Table_X2
(
Id int PRIMARY KEY IDENTITY
,Version_Value varchar(100)
)
GO
CREATE TABLE #Table_X3
(
Id int PRIMARY KEY IDENTITY
,Version_Value varchar(100)
)
GO
Do this one time:
CREATE TABLE Table_X
(
Id int PRIMARY KEY IDENTITY
,Version_Value varchar(100)
,X_number INT
)
GO
Then instead of doing this:
INSERT INTO #Table_X1 VALUES ('Val_X1_1'),('Val_X1_2'),('Val_X1_3')
INSERT INTO #Table_X2 VALUES ('Val_X2_1'),('Val_X2_2'),('Val_X2_3')
INSERT INTO #Table_X3 VALUES ('Val_X3_1'),('Val_X3_2'),('Val_X3_3')
Do this:
INSERT INTO Table_X VALUES ('Val_X1_1',1),('Val_X1_2',1),('Val_X1_3',1)
INSERT INTO Table_X VALUES ('Val_X2_1',2),('Val_X2_2',2),('Val_X2_3',2)
INSERT INTO Table_X VALUES ('Val_X3_1',3),('Val_X3_2',3),('Val_X3_3',3)
Much easier to query without dynamics:
--no
SELECT * FROM Table_X1
--yes
SELECY * FROM Table_X WHERE X_Number = 1
You've indicated you're stuck with it how it is, so you'll need to create and run your queries dynamically too. This is c#/vb flavored pseudocode:
string sql = "SELECT * FROM sometable"
for int x = 1 to 3
sql = sql + " table_x{x} on sometable.id = table_x{x}.id"
Or perhaps build a Union:
string sql = "WITH allx AS (SELECT * FROM table_x1"
for int x = 2 to 10
sql = sql + " UNION ALL SELECT * FROM table_x{x}"
sql = sql + ") select * from sometable inner join allx on..."
But I echo larnu's sentiments in the comments.. if you truly cannot change the tables that are created, consider creating a VIEW in a similar way to the UNION code above, that will sit alongside X number of tables and will provide a way to query without dynamic:
CREATE VIEW AllX AS(
SELECT x.*, 1 as Which FROM TABLE_X1 x
UNION ALL SELECT x.*, 2 as Which FROM TABLE_X2 x
UNION ALL SELECT x.*, 3 as Which FROM TABLE_X3 x
...
Use the same technique that creates 10 tables to string together a CREATE VIEW statement that views over the 10 tables, then you can query the view without Dynamic sql generation

Select form a lot of many-to-many relations at once best practice

I'm wondering what is the best approach to handle the following problem:
I've got a DB-Structure where many tables are linked to my Person table like this:
phone n-n person_phone_realtion n-n person n-n person_email_realtionn-n email
I want to query my tables and parse the result to JSON and store the many to many values inside arrays. Is it better to make only one trip to the database and parse the result of my JOIN-query (see example below), which can be quite large due to duplicates, to my desired schema or should I make more trips to the database and keep the query result small?
What is the best practices for this scenario
Created with the following statement:
DROP TABLE IF EXISTS phone CASCADE;
DROP TABLE IF EXISTS email CASCADE;
DROP TABLE IF EXISTS person CASCADE;
DROP TABLE IF EXISTS person_phone_realtion CASCADE;
DROP TABLE IF EXISTS person_email_realtion CASCADE;
CREATE TABLE phone (
phon_id text NOT NULL,
CONSTRAINT phone_pk PRIMARY KEY (phon_id)
);
CREATE TABLE email (
emai_id text NOT NULL,
CONSTRAINT email_pk PRIMARY KEY (emai_id)
);
CREATE TABLE person (
pers_id INTEGER NOT NULL,
CONSTRAINT person_pk PRIMARY KEY (pers_id)
);
CREATE TABLE person_phone_realtion (
pers_id int NOT NULL,
phon_id int NOT NULL
);
CREATE TABLE person_email_realtion (
pers_id int NOT NULL,
email_id int NOT NULL
);
INSERT INTO person(pers_id)
VALUES (1),(2),(3),(4),(5);
INSERT INTO email(emai_id)
VALUES ('a'),('b'),('c');
INSERT INTO phone(phon_id)
VALUES ('D'),('E'),('F');
INSERT INTO person_email_realtion(pers_id, email_id)
VALUES (1,'a'),(1,'b'), (1,'c'),(2,'b'),(3,'c');
INSERT INTO person_phone_realtion(pers_id, phon_id)
VALUES (1,'D'),(2,'D'), (2,'E'),(5,'F');
DROP TABLE IF EXISTS phone CASCADE;
DROP TABLE IF EXISTS email CASCADE;
DROP TABLE IF EXISTS person CASCADE;
DROP TABLE IF EXISTS person_phone_realtion CASCADE;
DROP TABLE IF EXISTS person_email_realtion CASCADE;
CREATE TABLE phone (
phon_id text NOT NULL,
CONSTRAINT phone_pk PRIMARY KEY (phon_id)
);
CREATE TABLE email (
emai_id text NOT NULL,
CONSTRAINT email_pk PRIMARY KEY (emai_id)
);
CREATE TABLE person (
pers_id INTEGER NOT NULL,
CONSTRAINT person_pk PRIMARY KEY (pers_id)
);
CREATE TABLE person_phone_realtion (
pers_id int NOT NULL,
phon_id int NOT NULL
);
CREATE TABLE person_email_realtion (
pers_id int NOT NULL,
email_id int NOT NULL
);
INSERT INTO person(pers_id)
VALUES (1),(2),(3),(4),(5);
INSERT INTO email(emai_id)
VALUES ('a'),('b'),('c');
INSERT INTO phone(phon_id)
VALUES ('D'),('E'),('F');
INSERT INTO person_email_realtion(pers_id, email_id)
VALUES (1,'a'),(1,'b'), (1,'c'),(2,'b'),(3,'c');
INSERT INTO person_phone_realtion(pers_id, phon_id)
VALUES (1,'D'),(2,'D'), (2,'E'),(5,'F');
Now I can query all the relations at once using JOIN wich would result in a lot of duplicate content:
SELECT * FROM person
RIGHT JOIN person_phone_realtion
ON person.pers_id = person_phone_realtion.pers_id
RIGHT JOIN phone
ON person_phone_realtion.phon_id = phone.phon_id
RIGHT JOIN person_email_realtion
ON person.pers_id = person_email_realtion.pers_id
RIGHT JOIN email
ON person_email_realtion.email_id = email.emai_id;
where I will get a result similar to this:
pers_id phon_id emai_id
1 D a
1 D b
1 D c
2 E b
2 D b
The resulting JSON should look like this:
[
{
"person" : 1,
"email": [
"a", "b", "c"
],
"phone":[
"D"
]
},
{
"person" : 2,
"email": [
"b"
],
"phone":[
"D", "E"
]
}
]
One trip to the database is usually best. You should pre-aggregate the values along each dimension:
select p.*, pp.phones, pe.emails
from person p left join
(select pers_id, array_agg(ppr.phone_id) as phones
from person_phone_realtion ppr
group by pers_id
) pp
on p.pers_id = pp.pers_id left join
(select pers_id, array_agg(per.email_id) as emails
from person_email_realtion ppr
group by pers_id
) pe
on p.pers_id = pe.pers_id ;
You can aggregate into strings or JSON, if you prefer.

Complex SQL Count Query

Hello I've been stuck with one SQL query for my assignment and was hoping for some help.
I need to get the Project ID for the best executed project -the project where (VERY_GOOD record count + GOOD record count) - (VERY_BAD record count + BAD record count) is greatest
My schema and test records in database (HSQLDB)
CREATE TABLE
PROJECT
(
ID IDENTITY NOT NULL PRIMARY KEY,
PROJECT_NAME VARCHAR(255) NOT NULL
);
CREATE TABLE
RECORD
(
ID IDENTITY NOT NULL PRIMARY KEY,
RESULT VARCHAR(255) NOT NULL,
);
CREATE TABLE
RECORD_PROJECT
(
PROJECT_ID INTEGER NOT NULL,
RECORD_ID INTEGER NOT NULL,
PRIMARY KEY(PROJECT_ID, RECORD_ID),
FOREIGN KEY (PROJECT_ID) REFERENCES PROJECT(ID) ON DELETE CASCADE,
FOREIGN KEY (RECORD_ID) REFERENCES RECORD(ID)
);
And test data:
INSERT INTO PROJECT (PROJECT_NAME) VALUES ('Bake a cake');
INSERT INTO PROJECT (PROJECT_NAME) VALUES ('Clean the house');
INSERT INTO RECORD (RESULT) VALUES ('GOOD');
INSERT INTO RECORD (RESULT) VALUES ('VERY_GOOD');
INSERT INTO RECORD (RESULT) VALUES ('VERY_GOOD');
INSERT INTO RECORD (RESULT) VALUES ('BAD');
INSERT INTO RECORD (RESULT) VALUES ('VERY_BAD');
INSERT INTO RECORD_PROJECT (PROJECT_ID, RECORD_ID) VALUES (0,0);
INSERT INTO RECORD_PROJECT (PROJECT_ID, RECORD_ID) VALUES (1,1);
INSERT INTO RECORD_PROJECT (PROJECT_ID, RECORD_ID) VALUES (1,2);
INSERT INTO RECORD_PROJECT (PROJECT_ID, RECORD_ID) VALUES (0,3);
INSERT INTO RECORD_PROJECT (PROJECT_ID, RECORD_ID) VALUES (1,4);
(I removed unrelated fields from tables)
So with this data I have 3 good records and 2 bad, I would need to get the project which has the highest 'rating', which according to this right now would be Clean the house with 3 good ratings over 2 negative for other project.
Maybe someone would figure this out, thanks!
That should be the (not testet) SQL in MySQL-Dialect:
SELECT rp.PROJECT_ID, p.PROJECT_NAME
SUM(CASE WHEN rp.RECORD_ID < 3 THEN 1 ELSE 0 END) AS rating
FROM RECORD_PROJEKT AS rp
JOIN PROJECT AS p ON p.ID = rp.PROJECT_ID
GROUP BY rp.PROJECT_ID
ORDER BY rating DESC

EAV Select query from spreaded value tables

I have the following SQL Server database structure I have to use to query data. The model could be wrong; I appreciate arguments if that's the case so I can ask for changes. If not, I need a query to get tabbed data in the format I will detail below.
The structure goes like this:
CLIENTS:
ClientID ClientName
-----------------------
1 James
2 Leonard
3 Montgomery
ATTRIBUTES:
AttributeID AttributeName
-----------------------------
1 Rank
2 Date
3 Salary
4 FileRecordsAmount
ATTRIBUTES_STRING:
ClientID AttributeID AttributeStringValue
1 1 Captain
2 1 Chief Surgeon
3 1 Chief Engineer
ATTRIBUTES_NUMERIC:
ClientID AttributeID AttributeNumericValue
1 4 187
2 4 2
3 4 10
The result I need would be the following:
RESULTS:
----------------------------------------------------------
ClientID ClientName Rank FileRecordsAmount
1 James Captain 187
2 Leonard Chief Surgeon 2
3 Montgomery Chief Engineer 10
How can I achieve this?
Thank you very much!
EDIT: The challenging issue here (for me) is that the attributes are dynamic... I have 5 tables of attributes (ATTRIBUTES_STRING, ATTRIBUTES_NUMERIC, ATTRIBUTES_DATE, ATTRIBUTES_BIT, ATTRIBUTES_INT) and the user should be able to set up it's own attributes.
You need an SQL join. It will look something like this:
select
CLIENTS.ClientID,
CLIENTS.ClientName,
ATTRIBUTES_STRING1.AttributeStringValue as Rank,
ATTRIBUTES_NUMERIC2.AttributeNumericValue as FileRecordsAmount
from
CLIENTS,
ATTRIBUTES ATTRIBUTES1,
ATTRIBUTES ATTRIBUTES2,
ATTRIBUTES_STRING ATTRIBUTES_STRING1,
ATTRIBUTES_NUMERIC ATTRIBUTES_NUMERIC2
where CLIENTS.ClientID = ATTRIBUTES_STRING1.ClientID
and CLIENTS.ClientID = ATTRIBUTES_NUMERIC2.ClientID
and ATTRIBUTES_STRING1.AttributeID = ATTRIBUTES1.AttributeID
and ATTRIBUTES_NUMERIC2.AttributeID = ATTRIBUTES2.AttributeID
and ATTRIBUTES1.AttributeName = 'Rank'
and ATTRIBUTES2.AttributeName = 'FileRecordsAmount'
;
Here is the SQL Fiddle for reference. This is my first EAV schema so I wouldn't put too much trust in it :)
Edit: Schema provided below for reference:
create table CLIENTS (
ClientID integer primary key,
ClientName varchar(50) not null
);
insert into CLIENTS values (1,'James');
insert into CLIENTS values (2,'Leonard');
insert into CLIENTS values (3,'Montgomery');
create table ATTRIBUTES (
AttributeID integer primary key,
AttributeName varchar(50) not null
);
create index ATTRIBUTE_NAME_IDX on ATTRIBUTES (AttributeName);
insert into ATTRIBUTES values (1,'Rank');
insert into ATTRIBUTES values (2,'Date');
insert into ATTRIBUTES values (3,'Salary');
insert into ATTRIBUTES values (4,'FileRecordsAmount');
create table ATTRIBUTES_STRING (
ClientID integer,
AttributeID integer not null,
AttributeStringValue varchar(255) not null,
primary key (ClientID, AttributeID)
);
insert into ATTRIBUTES_STRING values (1,1,'Captain');
insert into ATTRIBUTES_STRING values (2,1,'Chief Surgeon');
insert into ATTRIBUTES_STRING values (3,1,'Chief Engineer');
create table ATTRIBUTES_NUMERIC (
ClientID integer,
AttributeID integer not null,
AttributeNumericValue numeric(10, 5) not null,
primary key (ClientID, AttributeID)
);
insert into ATTRIBUTES_NUMERIC values (1,4,187);
insert into ATTRIBUTES_NUMERIC values (2,4,2);
insert into ATTRIBUTES_NUMERIC values (3,4,10);
Edit: Modified the select to make it easier to extend with extra attributes

How to avoid bad data entries in table through proper Key relationship?

I have 3 tables. widgets, widget types, widget type ID. I have created direct relationship between them. How can I avoid bad data going into Widget tables based on Widget_type and Widget_sub_Type. Please see my code. There is just one small thing missing.
Create table widgets (
Widget_ID int not null primary key,
Widget_type_ID int not null,
Widget_Sub_type_ID int,
Widget_Name varchar (50)
)
Create table Widget_Type (
Widget_Type_ID int not null primary key,
)
Create table Widget_Sub_type (
Widget_Sub_Type_ID int not null primary key,
Widget_Type_ID int not null
)
---adding foregin key constraints
Alter table widgets
ADD constraint FK_Widget_Type
FOREIGN KEY (Widget_type_ID)
References Widget_Type (Widget_type_ID)
Alter table widgets
ADD constraint FK_Widget_Sub_Type
FOREIGN KEY (Widget_Sub_type_ID)
References Widget_SUB_Type (Widget_SUB_type_ID)
Alter table widget_Sub_Type
ADD Constraint FK_Widget_Type_Alter
Foreign key (widget_Type_ID)
References Widget_Type (Widget_Type_ID)
---- insert values
insert Widget_Type values (1)
insert Widget_Type values (5)
insert Widget_Sub_type values (3,1)
insert Widget_Sub_type values (4,1)
insert Widget_Sub_type values (7,5)
insert Widget_Sub_type values (9,5)
-- This will error out which is correct
insert Widget_Sub_type values (5,6)
select * from Widget_Sub_type
select * from Widget_type
--Good
insert widgets (Widget_ID,Widget_Name, Widget_type_ID, Widget_Sub_type_ID)
values (1, 'TOY', 1, 3)
select * from widgets
--Good
insert widgets (Widget_ID,Widget_Name, Widget_type_ID, Widget_Sub_type_ID)
values (2, 'BatMan', 5, 7)
-- How to prevenet this, 3 is not sub_type_id of type_ID 5. This is bad data, It should not be inserted.
insert widgets (Widget_ID,Widget_Name, Widget_type_ID, Widget_Sub_type_ID)
values (3, 'Should Not', 5, 3)