Proper query or proper normalization: MySQL - mysql

What I am trying to do is to access the proper row in a table, so that it returns the right location name, address, etc. The address is returned correctly, but there are three results rather than one. These are our various international locations.
Either my tables aren't normalized properly or I'm writing my query incorrectly. I can't figure out which. Maybe a little of both. Here are my tables:
DEALERS TABLE:
channel_partner_id company
------------------ --------
626 Company Inc.
626 Company GmBH
626 Company Ltd.
DEALERS_LOCATIONS TABLE:
channel_partner_id location_id
------------------ -----------
626 18
626 19
626 20
LOCATIONS TABLE:
location_id address name_url
---------- -------------------- -------
18 1234 Anywhere St. anywhere-st
19 3245 Nowhere St. nowhere-st
20 90 Everywhere St. everywhere-st
I want to join them on the name_url.
So here's my query in CodeIgniter/Active Record (which is translated into standard MySQL easy enough):
$this->db->where('l.name_url', $name_url);
$this->db->join('all_dealers_locations dl', 'dl.channel_partner_id = d.channel_partner_id', 'inner');
$this->db->join('all_locations l', 'l.location_id = dl.location_id', 'inner');
$row = $this->db->get('all_dealers d')->row();
But I get back three results from this. Why, if I am using the where clause of the name_url (which is being passed properly into the function)? Is it the type of join I have? I tried left and outer and that didn't help.
What am I doing wrong?

Your tables have some issues.
The big red flag is that dealers has multiple rows with the same id (which means that channel_partner_id can not be the primary key).
This is a problem because it looks like dealers_locations is supposed to be an intersection table. The standard way to implement such a table is to take the primary keys of the two tables, which in this case would be locations and dealers. Since dealer_locations doesn't have the primary key of dealers, it's not going to work.
Here's how I would implement this instead:
create table dealers (
channel_partner_id int primary key,
name varchar(30)
);
create table locations (
location_id int primary key,
address varchar(30),
name_url varchar(30)
);
create table dealer_locations (
location_id int,
foreign key (location_id) references locations(location_id),
channel_partner_id int,
foreign key (channel_partner_id) references dealers(channel_partner_id),
primary key (location_id, channel_partner_id)
);
Note the two-part primary key for dealer_locations.

Related

Make sure no duplicates in "parent" of parent

I'm using MariaDB and PHPMyAdmin, but my code is all neutral so happy to switch to Postgres or whatever, but this should be straight forward enough. I haven't designed anything yet, just after best approaches.
I have 3 database tables that describes a premises. Let's say a hotel.
This theoretical hotel has multiple venues - 2 restaurants and a bar. Each of those has a few different rooms/areas. In these rooms are tables that customers can sit at.
In SQL, I imagine the tables would look like this
Venues
Venue ID
Venue Name
1
Restaurant 1
2
Restaurant 2
3
Bar
Rooms
Room ID
Room Name
Parent Venue (foreign key)
1
Patio
1
2
Function Room
1
3
Alcove
3
4
Private Dining
2
Tables
Table ID
Table Name
Parent Room (foreign key)
1
Table 1
1
2
Table 2
1
3
Table 3
1
4
Table 4
2
5
Table 1
3
6
Table 2
3
7
Table 3
3
8
Table 4
3
9
Table 1
4
10
Table 2
4
11
Table 3
4
I hope that data is correct :p
What I want to do is define a relationship whereas it's impossible to add a Table Name if it already exists in that venue. It doesn't matter what room the table is in.
E.g if I was to add another "Table 4", it would succeed in being entered if it was entered into Room 4, as Room 4 belongs to Restaurant 2, which does not already have a "Table 4". However if it was entered into any other room, it would fail as Restaurant 1 and Bar already have a "Table 4" in one of their rooms.
Now in the server side code this is fairly easy to check as I can do multiple queries or joins or a myriad of other ways, however I was wondering how to do this in SQL/PhpMyAdmin directly. I'm having a bit of trouble finding my way around MyAdmin.
Cheers
My recommendation is to redundantly include the parent venue in the tables table. So tables would have the additional column:
venueID
rooms would have a unique constraint (which is redundant):
alter table rooms add constraint unq_rooms_venueId
unique (venueId, roomId);
Then tables would have a unique constraint:
alter table tables add constraint unq_table_venueId
unique (tableName, venueId);
This solves the problem without having to resort to triggers.
What I'd do is switch from technical IDs to composite natural keys. You can use numbers for this (i.e. give a venue a number, a room a number, maybe a table a number) or use the names, if these are guaranteed not to change. E.g.:
venues
(
venue_name,
primary key (venue_name)
);
rooms
(
venue_name,
room_name,
primary key (venue_name, room_name),
foreign key (venue_name) references venues (venue_name)
)
tables
(
venue_name,
room_name,
table_name,
primary key (venue_name, room_name, table_name),
foreign key (venue_name, room_name) references rooms (venue_name, room_name),
unique constraint (venue_name, table_name)
)
(If using the names for keys and your tables don't contain any other information aside from the names, you can easily remove the tables venues and rooms of course and only keep the tables table, if you want.)
Same thing with numbers:
venues
(
venue_no,
name,
primary key (venue_no)
);
rooms
(
venue_no,
room_no,
name,
primary key (venue_no, room_no),
foreign key (venue_no) references venues (venue_no)
)
tables
(
venue_no,
room_no,
table_no,
name,
primary key (venue_no, room_no, table_no),
foreign key (venue_no, room_no) references rooms (venue_no, room_no),
unique constraint (venue_no, name)
)

Inputting multiple values into a relational database field

I am creating a database application for a restaurant stock management system. I currently have two database tables, one for the ingredients, and one for each dish.
Database tables and relationships
Each ingredient has a unique ID which is the PK of the table. And the 'ingredients' field in the DISH table is a foreign key linking to ingredient_ID. Each dish will obviously have many ingredients, but the ingredients can also belong to multiple dishes. I have read elsewhere about creating another table called ingredient_dish for example, but I'm not sure I fully understand how this works. Could somebody either point me in the right direction or attempt to explain please, and what sort of columns would I have in this joining table for the above tables?
Thanks a lot.
Since you have a Many-to-Many relationship between Ingredients and Dishes, you may want Ingredient_Dish to be a Cross-Reference table.
Its fields would include ingredient_id, a foreign key to the Ingredients table, and dish_id, a foreign key to the Dish table. Set the primary key of this new table to the combination of both fields. A creation command might look like the following, tailor it to suit:
CREATE TABLE Ingredient_Dish (
ingredient_id INT NOT NULL,
dish_id INT NOT NULL,
CONSTRAINT PK_Ingredients_by_dish PRIMARY KEY (ingredient_id, dish_id))
ALTER TABLE Ingredient_Dish
ADD CONSTRAINT FK_Ingredients FOREIGN KEY (ingredient_id) REFERENCES Ingredient (ingredient_id )
ALTER TABLE Ingredient_Dish
ADD CONSTRAINT FK_Dishess FOREIGN KEY (dish_id) REFERENCES Dish (dish_id)
Many-to-many relationships are best done with such an intermediate (or "bridge") table. The bridge table basically consists of the primary keys of the other tables to form a "bigger" foreign key, which allows you to implement a many-to-many relationship by providing primary key combinations.
Your use case (simplified) would necessitate tables like these:
dishes:
dish_id | other_columns
-------------------------
1 | ...
2 | ...
3 | ...
ingredients:
ingredient_id | other_columns
-------------------------------
1 | ...
2 | ...
3 | ...
dishes_ingredients_bridge:
dish_id | ingredient_id
-------------------------
1 | 1
1 | 2
2 | 2
2 | 3
3 | 3
This means you have 3 dishes and 3 ingredients, where
dish 1 consists of ingredients 1 and 2
dish 2 consists of ingredients 2 and 3
dish 3 consists only of ingredient 3
ingredient 1 is used only in dish 1
ingredient 2 is used in dishes 1 and 2
ingredient 3 is used in dishes 2 and 3
Also, make sure, the combinations in your bridge are unique (create a multi-column unique key). Omitting this usually doesn't result in big problems, but in some edge cases your application could behave in an undefined or unexpected way.
Now you can query (for example) all ingredients of dish 1 like this:
SELECT i.*
FROM ingredients as i
LEFT JOIN dishes_ingredients_bridge dib
ON dib.ingredient_id = dib.ingredient_id
LEFT JOIN dishes as d
ON d.dish_id = dib.dish_id
"Connecting" a dish to an ingredient means adding a row to the bridge. "Disconnecting" a dish from an ingredient means deleting such rows, respectively.
Hope this helps.
You are basically describing a bridge table. It's the only way to describe a many-to-many relationship without concatenating values (which is a very nasty can of worms). I suggest you read this further:
http://www.kimballgroup.com/2012/02/design-tip-142-building-bridges/
Hope it helps.

Foreign key constraints and bridging tables

I am currently working with 6 tables: users, categories, videogames, videogames_categories_bridge, users_favorites, users_dislikes. I am trying to layout the tables in the best manner possible to show video games preference for user(see below example). However, I am getting a foreign key constraint error when creating the tables. How could I achieve(if possible) the below with my current tables schema? Also, Is there a way in avoiding that both values inserted(favorite and dislike) are marked true for a game? SQLFIDDLE
Example: Show all video game preference for an userid 569723
game_id category_id game_name category_name favorite dislike
------- ----------- ---------------- ------------- --------- --------
840832 1000 'counter-strike' fps 1 NULL
779343 1000 'call of duty modern warfare' fps 1 NULL
684244 2000 'minecraft' adventure NULL NULL
983565 2000 'assassin\'s creed syndicate'adventure NULL NULL
858168 3000 'need for speed - rivals' racing NULL NULL
819837 4000 'mortal kombat x' fighting NULL NULL
634266 5000 'street fighter v' fighting NULL NULL
You have some problems with your foreign keys and tables in general:
the "destination" column of the foreign key reference has to be indexed so InnoDB can quickly check if it exists etc. (for instance user_id in your users table is only a second column in your primary key, it has to be first in some index)
in one case (videogames_categories_bridge.category_id) you try to reference the same column in the same table, that does not make sense
primary keys in users and categories contain the name AND id at the same time so they do not enforce much - usually the ID is the right one for a foreign key. The way you defined it there might be the same id for multiple different names.
http://sqlfiddle.com/#!9/9e24b - the FKs modified to work

Database design issue - visibility of content

I'm having some trouble determining what kind of approach should I take designing this database and I cant figure it.
The app will show some items to the user if he's given access to see them. Different users different access (possible to have access to the same item)
When user is logged in. First he will be presented with list of items that he has access to see.
Then he clicks on one item and goes to a list of versions of that item that he has access to see (not necessarily all of them)
Then he clicks on the version of item and he is presented with list of subversions of that version that he has access to see
So, different users, different access restrictions and admin can make changes on who sees what versions, subversions and items
Items
+*Item1*
-*ItemN*
*-Version1*
-*Subversion1*
Picture1
Picture2...
+*ItemN+1*
My question is how to design tables for this kind of database (how many, how to connect them etc)
Thank you
You will need to have multiple tables. I see the structure like this:
TABLE 1 ITEMS
each version and sub-version of an item is considered an item.
+---------+--------------+--------------+--------
| ITEM_ID | DESCRIPTION | DATA_FIELD_2 | etc ...
+---------+--------------+--------------+--------
| .. | .. | . | ...
TABLE 2 ITEMS_TREE
this table contains all the relations between items (and versions)
+---------+--------------+--------------+----------+
| TREE_ID | FATHER_ID | SELF | ORDER |
+---------+--------------+--------------+----------+
| .. | .. | . | ... |
where FATHER_ID and SELF are foreign keys connected to ITEM_ID Primary and unique key.
FATHER_ID will be NULL for root nodes (items),
SELF will refer to the node itself (item or whatever).
A version will have FATHER_ID that is the ITEM_ID of the item and so on for how many levels you want.
You will need a table of users and a table of permissions in which you can add the single item a user will be able to see, for example:
+---------------+---------+---------+
| PERMISSION_ID | USER_ID | ITEM_ID |
+---------------+---------+---------+
| ... | .. | .. |
That will contain a row for each permission. If you want to have group of people seeing the same collection of items the you can use group and have a different way to handle permissions, avoiding too much record in the database.
An example can be using a GROUP_ID instead of USER_ID, you are inserting some kind of item in the visible list of that group.
I hope this can be useful to you, let me know what you think about it
create table items
( -- endless self-join hierarchy
itemId int auto_increment primary key,
parent int not null, -- 0 if no parent, avoid nulls
itemName varchar(200) not null, -- "Our Wedding (My 2nd, your 1st)"
pathToFile varchar(200) null -- "/users/all/jpg/miama.jpg"
);
create table users
( userId int auto_increment primary key,
fullname varchar(100) not null
);
create table items_users_rights_junction
( id int auto_increment primary key,
itemId int not null,
userId int not null,
rightsMask int not null, -- here is your Visibility
-- FK's to enforce referential integrity:
CONSTRAINT fk_rights_items
FOREIGN KEY (itemId)
REFERENCES items(itemId),
CONSTRAINT fk_rights_users
FOREIGN KEY (userId)
REFERENCES users(userId),
unique key (itemId,userId), -- no dupes on combo
key (userId,itemId) -- go the other way, benefits by covered index
);
Subversioning is baked in (naturally) to the items hierarchy. Populate and query at will. Self-join knowledge would be helpful.
To make it a very useful covered index on (user_id,itemId,rightsMask) that would never need to go to data page for rights. Rather all is in the index with left-most being userId. This covering index could still be considered thin.
A covering index refers to the case when all columns selected are
covered by an index, in that case InnoDB (not MyISAM) will never read
the data in the table, but only use the data in the index,
significantly speeding up the select.
Some sample data for the first two tables:
insert users (fullName) values ('Kim Jones'),('Harry Smith');
truncate table items; -- will be run a few times to get demo data right
insert items (parent,itemName,pathToFile) values (0,'Our Wedding',null); -- id #1 top level no parent
insert items (parent,itemName,pathToFile) values (1,'Our Wedding - Cake pictures',null); #2 place holder
insert items (parent,itemName,pathToFile) values (1,'DJ spins the tunes',null); -- #3 place holder
insert items (parent,itemName,pathToFile) values (2,'She smears cake','/users/all/jpg/miama.jpg'); -- #4
insert items (parent,itemName,pathToFile) values (2,'He feeds the bride','/users/all/jpg/cake123.jpg'); -- #5
insert items (parent,itemName,pathToFile) values (5,'He feeds the bride take 2','/users/all/jpg/cake123-2.jpg'); -- #6
insert items (parent,itemName,pathToFile) values (5,'He feeds the bride take 3','/users/all/jpg/cake123-3.jpg'); -- #7

regular expression how to match with backreference two mixed type string

Assume that i have two strings like the following.
$sa = "12,20,45"; $sb = "13,20,50";
I want to check whether any of the number in sa present in sb with back reference so that i can get those numbers back and do some calculation.
The numbers are nothing but unique id's in database. So i am checking whether the ids in sa is present in the list of ids in sb.
Besides if it is possible to get all matching and non matching ids then that would be nice.
For this it doesn't have to be one operation. Multiple operations is fine.(like executing match twice or more).
What i am trying to do is i am creating subscribers and they are assigned to groups.
I create newsletters and will assign to groups.
If i try to assign a newsletter to the same group then i want the group id so that i can exempt that group and assign that newsletter to the rest.
so if group 15,16,17 are already assigned with a newsletter and the next time i am trying to assign group 15,20,21 i want 15 to be exempted and i want the newsletter to be assigned to 20,21.
And... If i could get a mysql example too then that could be nice.
Any type of answer if it could help the please post it.
THX
first of all, this is not a problem you would want to solve with regex. At.all.
Second, you shouldn't have a list of Ids as values in your database, especially if you need to look up on them. It's inefficient and bad database design.
If you only require to link subscribers to newletters these would be the tables you need, one table per entity and a junction table for joining. I have left out the foreign key constraints.
CREATE TABLE Subscribers
(subscriber_id bigint,
first_name varchar(50),
... )
CREATE TABLE Newsletter
(news_letter_id bigint,
name varchar(50),
... )
CREATE TABLE NewslettersSubscribers [or just "Subscriptions"]
(news_letter_id bigint,
subscriber_id bigint,
payment_type smallint,
...[other variables that are specific to this subscription]
)
If you would rather have your subscribers in a group and each subscriber can be in many groups, it would look like this.
CREATE TABLE Subscribers
(subscriber_id bigint,
first_name varchar(50)
... )
CREATE TABLE Group
(group_id bigint,
group_name varchar(50),
... )
CREATE TABLE SubscribersGroups --[or just "Membership"]
(subscriber_id bigint,
group_id bigint,
payment_type smallint,
--...[other variables that are specific to this membership]
)
CREATE TABLE Newsletter
(news_letter_id bigint,
name varchar(50),
... )
CREATE TABLE NewslettersGroups --[or just "SubscriptionGroups"]
(news_letter_id bigint,
group_id bigint
--...[possibly variables that are specific to this subscription group]
)
Now your actions are rather simple. In your example we have newsletter 1, and we have groups 15, 16, 17, 20 and 21 and possibly other groups. We also have these values in NewslettersGroups
| news_letter_id | group_id |
| 1 | 15 |
| 1 | 16 |
| 1 | 17 |
Now you want to connect newsletter 1 to 20 and 21 (only you think you need to do 15 as well). So just insert where it's needed (I'm not 100% sure if this syntax works, I don't use MySQL, but see this reference)
INSERT INTO NewslettersGroups VALUES (1,15),(1,20), (1,21)
ON DUPLICATE KEY UPDATE;