Is it possible to inherit data from another table across schemas? - sql

I'm trying to create a custom list of houses that a user can plot on a map. Each user can create houses, or edit the houses they've created. However there is a common set of houses that all users must have, and that can not be edited or deleted.
The users are divided into one schema per user, with a user.houses table listing all the custom houses for a single user.
Similarly, the public.houses table holds the houses available to every user. The structure of these tables are identical.
Is there a way to not duplicate the public.houses table data across all the user.houses tables?
I was able to use inheritance to ensure that the user.houses table exactly matches the public.houses table structure. Now I want to do this with the data as well.
INSERT INTO public.houses id VALUES (1), (2) ;
INSERT INTO user_a.houses id VALUES (3) ;
INSERT INTO user_b.houses id VALUES (4) ;
SELECT id FROM user_b.houses ;
I expect the output to be ids: [1, 2, 4]

This is too long for a comment.
You may have already made a bad decision by having one schema per user. There are some valid reasons for doing this -- security would be the number one reason. If you had a strong requirement that the data be separated. Another reason would be if the users were actually using different database versions (their own installations).
That said, it is usually better to support multiple users within a single databases/schema. The tables would identify the user for each entity (where it is important). Some tables, such as reference tables would be shared without ownership.
One thing that you can do is create views. The view:
create view v_user_houses as
select p.*
from public.houses p
union all
select u.*
from user.houses u;
You would need a separate view for each user.
An alternative would be to copy the publish houses into all the users table. When anything changes in the pubic houses, then you would propagate those changes to the users houses tables.

Try this
SELECT array_agg(a.id) FROM (
select id from public.houses
union all
select id from user_a.houses
union all
select id from user_b.houses ) AS a

Related

Do I really need a relation table in my case?

Lets say I have a module. I build an interface where user can assign the module to groups.
Lets say currently I have 3 groups. In the UI the user would choose all 3 groups to assign the module to them. For examle in a multiple selectbox.
The intention of the user is to assign the module to ALL groups.
I guess I would need a many to many relation table. My source code would execute a sql query to insert 3 entries.
But wait. What if two weeks later the admin adds a new group... In the relation table are only 3 entries. And the user wonders why the module is not assigned to the new added group.
What would be an elegant solution? I need definite to update the relation table, or I make a new column -called, lets say "groups"- in my module table where I add the assigned groupsIds in this format: "1;2;7;15" or the keyword "All".
The advantage would be that with the keyword "All" I could know in my code that the module is assigned to all groups.
With the relation table I do not have this option. In addition I do not need to assign a group to a module. I just need to assign a module to groups.
In my opinion I do not need a relation table in this case.
What would you say? Or do you have another approach?
To make a many-to-many relation, you need a relation table.
To use a solution like putting comma separated values in a field to make multiple relations only works if you fetch data from separate tables in separate queries, when you need to use that to join the data in the database, it becomes very complicated very fast.
In a relation table you could use a null value to mean "all". In this example the modules 1 and 2 are members of the group 1 only, and the module 3 is a member of all groups:
ModuleId GroupId
-------- -------
1 1
2 1
3 null
To fetch data for groups using a relation like that you would use a query like:
select
g.GroupName,
m.ModuleName
from
Groups g
inner join GroupModules gm on gm.GroupId is null or gm.GroupId = g.GroupdId
inner join Modules m on m.ModuleId = gm.ModuleId
Another alternative is to use the relation table as usual, and add a property on the module that it's a member of all groups. When you add a new group, you would also add records in the relation table for all modules that are members of all groups. Example (in T-SQL):
create procedure Group_Add
#GroupName varchar(50)
as
set nocount on
declare #id int
insert into Groups (
GroupName
) values (
#GroupName
)
set #id = scope_identity()
insert in GroupModules (
GroupId,
ModuleId
)
select
#id, ModuleId
from
Modules
where
IsInAllGroups = 1
The typical database pattern for this is 3 tables, Module, Group, GroupModule. That is how many to many relationships are properly handled in database design.
How to get the data populated is a problem for your UI. You can indeed have a pull down list that includes the word all for them to use in choosing the groups a module will be associated with. It can even be the default. What you do is write code to interpret ALL to insert one record for every group.
NOw if you are adding groups as well as modules, you also need a process to make sure that when a group is added all those which shoudl have all groups get added to the new group. Personally I would put an IS_ALL flag on the Module table to make this easier. Then you know which moduels have been selected for all groups. You will need to make sure that if someone goes back and changes the module to specificgroups instead of all that this field is updated.

How to use SQL Server views with distinct clause to Link to a detail table?

I may be total standard here, but I have a table with duplicate values across the records i.e. People and HairColour. What I need to do is create another table which contains all the distinct HairColour values in the Group of Person records.
i.e.
Name HairColour
--------------------
Sam Ginger
Julie Brown
Peter Brown
Caroline Blond
Andrew Blond
My Person feature view needs to list out the distinct HairColours:
HairColour Ginger
HairColour Brown
HairColour Blond
Against each of these Person feature rows I record the Recommended Products.
It is a bit weird from a Relational perspective, but there are reasons. I could build up the Person Feature"View as I add Person records using say an INSTEAD OF INSERT trigger on the View. But it gets messy. An alternative is just to have Person Feature as a View based on a SELECT DISTINCT of the Person table and then link Recommended Products to this. But I have no Primary Key on the Person Feature View since it is a SELECT DISTINCT View. I will not be updating this View. Also one would need to think about how to deal with the Person Recommendation records when a Person Feature record disappeared since since it is not based on a physical table.
Any thoughts on this please?
Edit
I have a table of People with duplicate values for HairColour across a number of records, e.g., more than one person has blond hair. I need to create a table or view that represents a distinct list of "HairColour" records as above. Against each of these "HairColour" records I need link another table called Product Recommendation. The main issue to start with is creating this distinct list of records. Should it be a table or could it be a View based on a SELECT DISTINCT query?
So Person >- HairColour (distinct Table or Distinct View) -< Product Recommendation.
If HairColour needs to be a table then I need to make sure it has the correct records in it every time a Person record is added. Obviously using a View would do this automatically, but I am unsure whether you can can hang another table off a View.
If I understand correctly, you need a table with a primary key that lists the distinct hair colors that are found in a different table.
CREATE TABLE Haircolour(
ID INT IDENTITY(1,1) NOT NULL,
Colour VARCHAR(50) NULL
CONSTRAINT [PK_Haircolour] PRIMARY KEY CLUSTERED (ID ASC))
Then insert your records. If this is querying a table called "Person" it will look like this:
INSERT INTO Haircolour (Colour) SELECT DISTINCT HairColour FROM Person
Does this do what you are looking for?
UPDATE:
Your most recent Edit shows that you are looking for a many-to-many relationship between the Person and ProductRecommendation tables, with the HairColour table functioning as a cross reference table.
As ErikE points out, this is a good opportunity to normalize your data.
Create the HairColour table as described above.
Populate it from whatever source you like, for example the insert statement above.
Modify both the Person and the ProductRecommendation tables to include a HairColourID field, which is an integer foreign key that points to the PK field of the HairColour table.
Update Person.HairColourID to point to the color mentioned in the Person.HairColour column.
Drop the Person.HairColour column.
This involves giving up the ability to put free form new color names into the Person table. Any new colors must now be added to the HairColour table; those are the only colors that are available.
The foreign key constraint enforces the list of available colors. This is a good thing. Referential integrity keeps your data clean and prevents a lot of unexpected errors.
You can now confidently build your ProductRecommendation table on a data structure that will carry some weight.
Are you simply looking for a View of distinct hair colors?
CREATE VIEW YourViewName AS
SELECT DISTINCT HairColour
FROM YourTableName
You can query this view like a table:
SELECT 'HairColour: ' + HairColour
FROM YourViewName
If you are trying to create a new (temp) table, the syntax would look like:
SELECT Name, HairColour
INTO #Temp
FROM YourTableName
GROUP BY Name, HairColour
Here the GROUP BY is doing the same work that a DISTINCT keyword would do in the select list. This will create a temp table with unique combinations of "Name" and "HairColour".
You need to clear up a few things in your post (or in your mind) first:
1) What are the objectives? Forget about tables and views and whatever. Phrase your objectives as an ordinary person would. For example, from what I could gather from your post:
"My objective is to have a list of recommended products based on each person's hair colour."
2) Once you have that, check what data you have. I assume you have a "Persons" table, with the columns "Name" and "HairColour". You check your data and ask yourself: "Do I need any more data to reach my objective?" Based on your post I say yes: you also need a "matching" between hair colours and product ids. This must be provided, or programmed by you. There is no automatic method of saying for example "brown means products X,Y,Z.
3) After you have all the needed data, you can ask: Can I perform a query that will return a close approximation of my objective?
See for example this fiddle:
http://sqlfiddle.com/#!2/fda0d6/1
I have also defined your "Select distinct" view, but I fail to see where it will be used. Your objectives (as defined in your post) do not make this clear. If you provide a thorough list in Recommended_Products_HairColour you do not need a distinct view. The JOIN operation takes care of your "missing colors" (namely "Green" in my example)
4) When you have the query, you can follow up with: Do I need it in a different format? Is this a job for the query or the application? etc. But that's a different question I think.

Restrict what a user can see in an SQL query

this is a bit out there, but I was wondering if it is possible to restrict a usergroup in SQL to only see certain subsets of data..
Say for example a table of products, ProductName and State are the two columns. I only want a usergroup to see products from their state, when they do a query like SELECT * FROM Products
Is that possible?
Restrict access to the underlying table.
And give your user groups access to views on these underlying tables.
So:
Given table:
CREATE TABLE EVERYTHING ID INTEGER, TYPE CHAR(1), SEMISECRET CHAR(20), REALLYSECRECT CHAR(20));
You can create views which give access to only certain columns:-
CREATE VIEW SOMESTUFF AS SELECT ID, TYPE, SEMISECRET FROM EVERTHINK;
You can create views which give access to certain rows:-
CREATE VIEW TYPESLICE AS SELECT ID, TYPE, REALLYSECRECT WHERE TYPE ='X';
Much more is possible, but that's the basics.
Create one or more views corresponding to your subsets and set the appropriate permissions on them.

What is the preferred way of saving dynamic lists in database?

In our application user can create different lists (like sharepoint) for example a user can create a list of cars (name, model, brand) and a list of students (name, dob, address, nationality), e.t.c.
Our application should be able to query on different columns of the list so we can't just serialize each row and save it in one row.
Should I create a new table at runtime for each newly created list? If this was the best solution then probably Microsoft SharePoint would have done it as well I suppose?
Should I use the following schema
Lists (Id, Name)
ListColumns (Id, ListId, Name)
ListRows (Id, ListId)
ListData(RowId, ColumnId, Value)
Though a single row will create as many rows in list data table as there are columns in the list, this just doesn't feel right.
Have you dealt with this situation? How did you handle it in database?
what you did is called EAV (Entity-Attribute-Value Model).
For a list with 3 columns and 1000 entries:
1 record in Lists
3 records in ListColumns
and 3000 Entries in ListData
This is fine. I'm not a fan of creating tables on-the-fly because it could mess up your database and you would have to "generate" your SQL queries dynamically. I would get a strange feeling when users could CREATE/DROP/ALTER Tables in my database!
Another nice feature of the EAV model is that you could merge two lists easily without droping and altering a table.
Edit:
I think you need another table called ListRows that tells you which ListData records belong together in a row!
Well I've experienced something like this before - I don't want to share the actual table schema so lets do some thought exercises using some of the suggested table structures:
Lets have a lists table containing a list of all my lists
Lets also have a columns table containing the metadata (column names)
Now we need a values table which contains the column values
We also need a rows table which contains a list of all the rows, otherwise it gets very difficult to work out how many rows there actually are
To keep things simple lets just make everything a string (VARCAHR) and have a go at coming up with some queries:
Counting all the rows in a table
SELECT COUNT(*) FROM [rows]
JOIN [lists]
ON [rows].list_id = [Lists].id
WHERE [Lists].name = 'Cars'
Hmm, not too bad, compared to:
SELECT * FROM [Cars]
Inserting a row into a table
BEGIN TRANSACTION
DECLARE #row_id INT
DECLARE #list_id INT
SELECT #list_id = id FROM [lists] WHERE name = 'Cars'
INSERT INTO [rows] (list_id) VALUES (#list_id)
SELECT #row_id = ##IDENTITY
DECLARE #column_id INT
-- === Need one of these for each column ===
SELECT #column_id = id FROM [columns]
WHERE name = 'Make'
AND list_id = #list_id
INSERT INTO [values] (column_id, row_id, value)
VALUES (#column_id, #row_id, 'Rover')
-- === Need one of these for each column ===
SELECT #column_id = id FROM [columns]
WHERE name = 'Model'
AND list_id = #list_id
INSERT INTO [values] (column_id, row_id, value)
VALUES (#column_id, #row_id, 'Metro')
COMMIT TRANSACTION
Um, starting to get a little bit hairy compared to:
INSERT INTO [Cars] ([Make], [Model}) VALUES ('Rover', 'Metro')
Simple queries
I'm now getting bored of constructing tediously complex SQL statements so maybe you can have a go at coming up with equivalent queries for the followng statements:
SELECT [Model] FROM [Cars] WHRE [Make] = 'Rover'
SELECT [Cars].[Make], [Cars].[Model], [Owners].[Name] FROM [Cars]
JOIN [Owners] ON [Owners].id = [Cars].owner_id
WHERE [Owners].Age > 50
SELECT [Cars].[Make], [Cars].[Model], [Owners].[Name] FROM [Cars]
JOIN [Owners] ON [Owners].id = [Cars].owner_id
JOIN [Addresses] ON [Addresses].id = [Owners].address_id
WHERE [Addresses].City = 'London'
I hope you are beginning to get the idea...
In short - I've experienced this before and I can assure you that creating a database inside a database in this way is definitely a Bad Thing.
If you need to do anything but the most basic querying on these lists (and literally I mean "Can I have all the items in this list please?"), you should try and find an alternative.
As long as each user pretty much has their own database I'll definitely recommend the CREATE TABLE approach. Even if they don't I'd still recommend that you at least consider it.
Perhaps a potential solution would be the creating of lists can involve CREATE TABLE statements for those entities/lists?
It sounds like the db structure or schema can change at runtime, or at the user's command, so perhaps something like this might help?
User wants to create a new list of an entity never seen before. Call it Computer.
User defines the attributes (screensize, CpuSpeed, AmountRAM, NumberOfCores)
System allows user to create in the UI
system generally lets them all be strings, unless can tell when all supplied values are indeed dates or numbers.
build the CREATE scripts, execute them against the DB.
insert the data that the user defined into that new table.
Properly coded, we're working with the requirements given: let users create new entities. There was no mention of scale here. Of course, this requires all input to be sanitized, queries parameterized, actions logged, etc.
The negative comment below doesn't actually give any good reasons, but creates a bit of FUD. I'd be interested in addressing any concerns with this potential solution. We haven't heard about scale, security, performance, or usage (internal LAN vs. internet).
You should absolutely not dynamically create tables when your users create lists. That isn't how databases are meant to work.
Your schema is correct, and the pluralization is, in my opinion, also correct, though I would remove the camel case and call them lists, list_columns, list_rows and list_data.
I would further improve upon your schema by skipping rows and columns tables, they serve no purpose. Simply have a row/column number attached to each cell, and keep things sparse: Don't bother holding empty cells in the database. You retain the ability to query/sort based on row/column, your queries will be (potentially very much) faster because the number of list_cells will be reduced, and you won't have to do any crazy joining to link your data back to its table.
Here is the complete schema:
create table lists (
id int primary key,
name varchar(25) not null
);
create table list_cells (
id int primary key,
list_id int not null references lists(id)
on delete cascade on update cascade,
row int not null,
col int not null,
data varchar(25) not null
);
It sounds like you might have Sharepoint already deployed in your environment.
Consider integrating your application with Sharepoint, and have it be your datastore. No need to recreate all the things you like about Sharepoint, when you could leverage it.
It'd take a bit of configuring, but you could call SP web services to CRUD your list data for you.
inserting list data into Sharepoint via web services
reading SP lists via web services
Sharepoint 2010 can also expose lists via OData, which would be simple to consume from any application.

How to display multiple values in a MySQL database?

I was wondering how can you display multiple values in a database for example, lets say you have a user who will fill out a form that asks them to type in what types of foods they like for example cookies, candy, apples, bread and so on.
How can I store it in the MySQL database under the same field called food?
How will the field food structure look like?
You may want to read the excellent Wikipedia article on database normalization.
You don't want to store multiple values in a single field. You want to do something like this:
form_responses
id
[whatever other fields your form has]
foods_liked
form_response_id
food_name
Where form_responses is the table containing things that are singular (like a person's name or address, or something where there aren't multiple values). foods_liked.form_response_id is a reference to the form_responses table, so the foods liked by the person who has response number six will have a value of six for the form_response_id field in foods_liked. You'll have one row in that table for each food liked by the person.
Edit: Others have suggested a three-table structure, which is certainly better if you are limiting your users to selecting foods from a predefined list. The three-table structure may be better in the case that you are allowing them the ability to enter their own foods, though if you go that route you'll want to be careful to normalize your input (trim whitespace, fix capitalization, etc.) so you don't end up with duplicate entries in that table.
normally, we do NOT work out like this. try to use a relation table.
Table 1: tbl_food
ID primary key, auto increment
FNAME varchar
Table 2: tbl_user
ID primary key, auto increment
USER varchar
Table 3: tbl_userfood
RID auto increment
USERID int
FOODID int
Use similar format to store your data, instead a chunk of data fitted into a field.
Querying in these tables are easier than parsing the chunk of data too.
Use normalization.
More specifically, create a table called users. Create another called foods. Then link the two tables together with a many-to-many table called users_to_foods referencing each others foreign keys.
One way to do it would be to serialize the food data in your programming language, and then store it in the food field. This would then allow you to query the database, get the serialized food data, and convert it back into a native data structure (probably an array in this case) in your programming language.
The problem with this approach is that you will be storing a lot of the same data over and over, e.g. if a lot of people like cookies, the string "cookies" will be stored over and over. Another problem is searching for everyone who likes one particular food. To do that, you would have to select the food data for each record, unserialize it, and see if the selected food is contained within. This is a very inefficient.
Instead you'll want to create 3 tables: a users table, a foods table, and a join table. The users and foods tables will contain one record for each user and food respectively. The join table will have two fields: user_id and food_id. For every food a user chooses as a favorite, it adds a record to the join table of the user's ID and the food ID.
As an example, to pull all the users who like a particular food with id FOOD_ID, your query would be:
SELECT users.id, users.name
FROM users, join_table
WHERE join_table.food_id = FOOD_ID
AND join_table.user_id = users.id;