Db pattern for finding the owner of a record - mysql

I'm looking for a pattern for dealing with a situation where a record in a table may be related to 1 and only one of several other records and I need to know which. It's difficult to describe so I will use an example with an oauth client from a recent project.
A client has an id and secret and teams or users may own clients
clients
client_id|client_secret
user_clients
user_id|client_id
team_clients
team_id|client_id
The issue here is when someone attempts to use client credentials to get an access token I need to know if those credentials belong to a team or a user. I'm not really sure of the best way to deal with this, I control the database so changes can be made.
Some options I've considered:
Modify clients and remove user_clients and team clients
clients
client_id|client_secret|team_id(nullable)|user_id(nullable)
Create a client owners table and remove user_clients and team_clients
client_owners
team_id(nullable)|user_id(nullable)|client_id
Adding a type to the clients table and doing 2 queries or a conditional query
clients
client_id|client_secret|type(user or team)
Left joins and filtering/mapping in code.
select * from clients c left join user_clients u ... left join team_clients t ... where c.client_id = ?
None of these options feel great so I am wondering if there is a better pattern. This is a simplified example, sometimes these objects are much more complicated and have many more possible relationships so the queries can get pretty hairy and also difficult to manage when using an orm (though requiring raw sql for parts of the system is not an issue).
I'd love to hear how people are solving this problem.
Thanks in advance.

Design
What you describe are 1:n relations:
One user can own multiple clients
One team can own multiple clients
So, the first approach seams appropriate: The client table has a user column and a client column.
Now you say a client can only be owned by either a user or a client. You would have a check contraint on the client table for this to ensure that only one of the IDs is set. (And if every client must have an owner, this constraint would also care about this.)
Some example queries
If you want to get all clients owned by users:
select * from clients where user_id is not null;
If you want to know whether a client is owned by a user or a team:
select
c.*,
case when user_id is not null then 'user'
when team_id is not null then 'team'
else 'none'
end as owner
from clients c;
If you want user or team information:
select c.*, coalesce(u.name, t.name) as owner_name
from clients c
left join users u on u.user_id = c.user_id
left join teams t on t.team_id = c.team_id;

The idea with the left joins is something you can go with. Leave your clients table with just clientID and left join it on user_clients and team_clients.
You could also combine that with your first option and add a column for user_clientsID and team_clientsID and at adding to the left join clients.user_clientsID <> 0 which is going to help you with the.
The 2nd and 3rd option is something I have seen used as well. With the second option perhaps being the one that can sometimes create the most headaches.
Also an option you can consider is creating a view table with all the information you might need

Related

MySQL 1:N Data Mapping

Something really bugs me and im not sure what is the "correct" approach.
If i make a select to get contacts from my database there are a decent amount of joins involved.
It will look something like this (around 60-70 columns):
SELECT *
FROM contacts
LEFT JOIN company
LEFT JOIN person
LEFT JOIN address
LEFT JOIN person_communication
LEFT JOIN company_communication
LEFT JOIN categories
LEFT JOIN notes
company and person are 1:1 cardinality so its straight forward.
But "address", "communication" and "categories" are 1:n cardinality.
So depending on the amount of rows in the 1:n tables i will get a lot of "double" rows (I don't know whats the real term for that, the rows are not double i know that the address or phone number etc is different). For myself as a contact, a fairly filled contact, i get 85 rows back.
How do you guys work with that?
In my PHP application i always wrote some "Data-Mapper" where the array key was the "contact.ID aka primary" and then checked if it exists and then pushed the additional data into it. Also PHP is not really type strict what makes it easy.
Now I'm learning GO(golang) and i thought screw that LOOOONG select and data mapping just write selects for all the 1:n.... yeah no, not enough connections to load a table full of contacts. I know that i can increase the connections but the error seems to imply that this would be the wrong way.
I use the following driver: https://github.com/go-sql-driver/mysql
I also tried GROUP_CONCAT but then i running in trouble parsing it back.
Do i have to do my mapping approach again or is there some nice solution out there? I found it quite dirty at points tho?
The solution is simple: you need to execute more than one query!
The cause of all the "duplicate" rows is that you're generating a result called a Cartesian product. You are trying to join to several tables with 1:n relationships, but each of these has no relationship to the other, so there's no join condition restricting them with respect to each other.
Therefore you get a result with every combination of all the 1:n relationships. If you have 3 matches in address, 5 matches in communication, and 5 matches in categories, you'd get 3x5x5 = 75 rows.
So you need to run a separate SQL query for each of your 1:n relationships. Don't be afraid—MySQL can handle a few queries. You need them.

Unique records in search query including many to one subquery

First time poster and enthusiastic Access newbie.
I've got a search screen based on Allen Browne's wonderful search in vba (http://allenbrowne.com/ser-62.html). This has worked great for most of my purposes, but now a child table is duplicating records.
Our clients(providers), can be enrolled in multiple programs. we've got four. I want a search that let's me filter by provider type, but not create duplicate records when a provider is enrolled in more than one provider type. In the example image, carmen titus is in the LEHRC and fccn programs, and therefore shows up twice. Tried to post pic but no dice.
Please help! I searched diligently and could not find a solution. I'd appreciate the support or to be pointed to a related post. I hope this makes sense. I think half my battle as a self-trained newbie is not knowing the terminology.
We'll need more info!
It sounds like the query you are basing the search on contains columns from two tables with a one to many relationship ie clients and "Client programs", such that a single client has zero to 4 programs.
It sounds like you only want to return a list of providers (ie rows on the one side), but your SQL is returning data from both tables.
Here's what you SQL might need to look like to do what you need:
SELECT *
FROM clients AS mainClient
WHERE
EXISTS
(SELECT 1
FROM clients AS C
LEFT JOIN ClientPrograms AS CP
ON C.ID = CP.ClientID
WHERE mainClient.ID = C.ID
' the above line links the EXISTS "Sub query" to the main query
AND client name like "*j*" ... etc...
... ie lots of criteria generated by you popup search criteria dialogue)
)
By adding the EXISTS statement the main query will be editable.
If you had used SQL like the following you would not be able to edit it
SELECT c.name, c.dob, etc.. ie all the field you want on the form whichwill all be from the client table
FROM clients AS C
LEFT JOIN ClientPrograms AS CP
ON C.ID = CP.ClientID
WHERE mainClient.ID = C.ID
' the above line links the EXISTS "Sub query" to the main query
AND client name like "*j*" ... etc...
... ie lots of criteria generated by you popup search criteria dialogue)
GROUP BY all the field in the select statement
I hope this gives you some inspiration

Modelling ownership in MySQL

I have a table Things and I want to add ownership relations to a table Users. I need to be able to quickly query the owners of a thing and the things a user owns. If I know that there will be at most 50 owners, and the pdf for the number of owners will probably look like this, should I rather
add 50 columns to the Things table, like CoOwner1Id, CoOwner2Id, …, CoOwner50Id, or
should I model this with a Ownerships table which has UserId and ThingId columns, or
would it better to create a table for each thing, for example Thing8321Owners with a row for each owner, or
perhaps a combination of these?
The second choice is the correct one; you should create an intermediate table between the table Things and the table Owners (that contains the details of each owner).
This table should have the thing_id and the owner_id as the primary key.
So finally, you well have 3 tables:
Things (the things details and data)
Owner (the owners details and data)
Ownerships (the assignment of each thing_id to an owner_id)
Because in a relational DB you should not have any redundant data.
You should definitely go with option 2 because what you are trying to model is a many to many relationship. (Many owners can relate to a thing. Many things can relate to an owner.) This is commonly accomplished using what I call a bridging table. (Which exactly what option 2 is.) It is a standard technique in a normalized database.
The other two options are going to give you nightmares trying to query or maintain.
With option 1 you'll need to join the User table to the Thing table on 50 columns to get all of your results. And what happens when you have a really popular thing that 51 people want to own?
Option 3 is even worse. The only way to easily query the data is to use dynamic sql or write a new query each time because you don't know which Thing*Owners table to join on until you know the ID value of the thing you're looking for. Or you're going to need to join the User table to every single Thing*Owners table. Adding a new thing means creating a whole new table. But at least a thing doesn't have a limit on the number of owners it could possibly have.
Now isn't this:
SELECT Users.Name, Things.Name
FROM Users
INNER JOIN Ownership ON Users.UserId=Ownership.UserId
INNER JOIN Things ON Things.ThingId=Ownership.ThingId
much easier than any of those other scenarios?

MySQL Tables Arrangement

I have a corporate website which is used to communicate between staff members as well as staff members with clients. There are internal users who can login and work with their mail using web interface, there is a list of external clients with email address and phone numbers which internal users can use to write an email or make a call. Sometimes clients become staff members, sometime staff members gets fired but stays in the database as they can become future clients.
There are two MySQL tables for those two types. First one is a full list of all people, and there is a separate table of internal users partially duplicating the first table. The second table for the people who can login so it has login, password and some permisisons fields but it also have last name, first name, job, address etc. which is already in the first table.
So all internal users have two entries - one entry at the users table and one at people table. People table has internal users and all the external clients data.
I'm thinking to make one table from those two just by adding internal users fields to the people table such as login, password, permissions etc. so whoever have those fields filled considered to be internal users. That would probably simplify my SQL queries and get rid of edless SQL JOIN constructions as I constantly have to fetch data from both of the tables in order to get full data on a user.
Basically I want users table to become part of the people table. Is there any negative consequences per your experience may be in terms of security or conviniency that can be a problem for such an integration of clients and users being put together in one table?
I don't think you should get rid of the users table. The distinction between internal and external users is too important to depend just on the use of columns.
Instead, make users a "subtype" of people. So keep the people table and include all the users in them, with the appropriate "people" fields. Then in your users table, include the internal information along with a people id.
With this structure, it is easy to get "everyone" (from people) and "internal users" (from users). To get external users, you need to do something like:
select p.*
from people p
where not exists (select 1 from users u where p.personid = u.personid);
This should be a fast operation, with a index on personid. You could maintain a flag in the people table, indicating whether someone is or is not a person, but you would need a trigger to keep it up-to-date. Probably not worth the effort.

Social Network SQL statement for posts

I have been working on a Social Network to further my knowledge with PHP, however I have come to the point where I will be pulling posts from he database depending on who the user is "friends" with.
My database structure for friends is as follows:
USER_A | USER_B | CREATED_AT | IS_BLOCKED
And then for the posts:
USER | UPDATE | STATUS
So, I want to show posts selected from my post table if the user logged in is friends with the user who posted it.
So, basically, wondering if anyone has any suggestions. It won't be anymore complicated than that, not interested in getting friends of friends or anything like that. So, just wondering if someone could point me in the right direction with this one.
I read it would require JOINs and such, however I have never really stepped into that side of SQL so therefore would have no idea what I would be doing with it, so links to relevant questions and articles on that, if required, would be appreciated.
Thanks
(I am essentially looking for help with the SQL statement, not with the PHP.)
Update #1:
I have got the newsfeed working, now I need to be able to join a statement to get the users names to be displayed. My current status is on this: http://sqlfiddle.com/#!2/b07793/1/0
Before anyone goes crazy about storing passwords in plaintext, this is not a public system, it is purely for me to test, and hashing passwords for a non-public script that will only ever be used by me with fake passwords is hardly a security risk, it just makes debugging a pain.
Something like this should work :
SELECT * FROM posts
JOIN friends ON posts.user = friends.user_b
WHERE user_a = 'someuser'
In this statement we are JOINING to tables : posts and friends, based on the value of two column, we are putting in relation the column user of the post table with the column user_b of the friends table, in this way we will obtain a result set with only the value of the two table where the two columns in relation are equal ...
If I understood correctly the db schema you described, this will bring you all posts that have been written by a user that is friends with the logged in user.
SELECT * FROM posts WHERE posts.user IN (SELECT user_b FROM friendships WHERE user_a=$logged_in_user)
The query is pretty simple. You get all the rows from table posts that the creator of that post has a line in your friendships table as user_b while user_a is set to be the logged in user.
I'm assuming a few things, if they're not correct you'll have to modify this stuff but if I'm right, these operations won't need any JOINs:
$userSession variable contains the user who's logged in's ID.
$userToView variable contains the user who he's trying to view's ID.
Your database structure will only have one entry for the two users who are friends, so we have to check both combinations.
To determine if the user is a friend:
SELECT user_a, user_b FROM friends
WHERE (user_a = $userSession AND user_b = $userToView)
OR (user_a = $userToView AND user_b = $userSession)
LIMIT 1;
Then you'd use an if statement in PHP (it's usually better to keep business logic in the application, rather than in the database), if it's true return the posts.
$results = $mysqli->query($query)->fetch_row();
if($results) {
... return posts ...
} else {
echo 'Not friends';
}
That would be a query something like this:
SELECT * FROM posts
WHERE user = $userToView;
You will need to read up about mysqli (do not use mysql plugin, it's deprecated) and about SQL injection attacks and how to prevent them. It may sound like a hassle, but it's far less daunting to understand this stuff than to deal with the fallout if you don't.