Creating View: Multiple Foreign Key Relations to Single Table - mysql

I have a table, tblNoComp, that has two columns, both foreign keys pointing to tblPackage.ID. The purpose of tblNoComp is to store which packages are not compatible with each other, by simply storing the ID of those packages in two columns, OneID and TwoID.
May not be the best way of storing it, but since multiple packages aren't compatible with others, it seemed to be the most logical.
Attempting to create a view that shows the tblPackage.Name for the two side by side - I have the following, but unsure how to get the TwoID Package Name..
SELECT tblNoComp.OneID, tblPackages.Package,tblNoComp.TwoID,tblPackages.Package
FROM tblNoComp, tblPackages
WHERE (tblNoComp.OneID = tblPackages.PID)
Currently the second tblPackages.Package is simply showing OneID name, not TwoID.. Not sure how to resolve?
Thank you!
--Apologies if a simple question, I've searched for an hour but haven't quite been able to describe my problem correctly.

The code you have in your comment:
SELECT
tblNoComp.OneID,
tblPackages.Package AS OneIDPackageName,
tblNoComp.TwoID,
tblPackages.Package AS TwoIDPackageName
FROM
tblNoComp
LEFT JOIN tblPackages
ON tblNoComp.OneID = tblPackages.PID
Is aliasing the columns instead of the tables. The idea behind the aliasing is to JOIN the same table twice as two different tables, using two different aliases. You're only joining it once and trying to use it twice.
You probably intent something more like this:
SELECT
tblNoComp.OneID,
tblOnePackages.Package AS OneIDPackageName,
tblNoComp.TwoID,
tblTwoPackages.Package AS TwoIDPackageName
FROM
tblNoComp
LEFT JOIN tblPackages AS tblOnePackages
ON tblNoComp.OneID = tblOnePackages.PID
LEFT JOIN tblPackages AS tblTwoPackages
ON tblNoComp.TwoID = tblTwoPackages.PID
(Note that I don't have a MySQL syntax checker handy, so this may need to be tweaked in order to run properly.)
Note that the same table is joined twice on two different keys, and that each time it's given a different alias so that it can be referenced within the SELECT clause as two separate tables.

Related

SQL Query relationship between 3 tables

I have a scenario where I have the following tables:
Inventories
delivery_items
deliveries
I seek a query where, having the inventory id, I get the delivery_item(fk_inventory),
which then I get the delivery from the (fk_delivery).
Manually, I go to the delivery_items table, then I search for the fk_inventory that matches the id from the inventory that I'm looking for,
then I get the fk_delivery, and get the delivery.
But I need to run a report on 15k+ items.
How to write a query where from a list of inventory ids I can get to the delivery following the relationship that I mentioned above?
There are many sites on writing SQL queries, differentiating between a normal (inner) join vs outer join, left join, right join, subqueries, etc. What you are looking to do is probably best (due to all inventory items in question) is simple joins.
Try to think of it this way, and maybe do it this way. Have a sheet of paper, one representing each table and write the columns on it.
Now, visually looking at the available tables, put them next to each other based on how they are related. Note the column in table A that is the foreign key to the next table. Then again, from the second to the third.
Once you have this done (or even if just mentally), you can SEE how they are related. This is the basis of the FROM clause
select *
from
YourFirstTable yft
JOIN YourSecondTable yst
on yft.WhateverKey = yst.MatchingKeyColumn
JOIN YourThirdTable ytt
on yst.KeyToThirdTable = ytt.KeyInThisTable
Now that you have all your relationships established, you can always declare the individual columns you want from those respective tables. Easier to use with the aliases such as I provided here via yft, yst, ytt representing the first, second and third tables. Use aliases appropriate to your tables such as i=inventories, di = delivery_items, d = deliveries.
Then add whatever FILTERING conditions you want. If the condition is based on the FIRST Table such as yft above, that would go into the WHERE clause such as
where
yft.SomeColumn = 'blah'
If the filtering criteria is specific to your second or third table, just add that to the JOIN / ON condition so it stays with the table and you know contextually it is associated HERE. It makes it easier when you are getting into LEFT JOINs.
from
YourFirstTable yft
JOIN YourSecondTable yst
on yft.WhateverKey = yst.MatchingKeyColumn
AND yst.SecondTableColumn = 'someOtherValue'
AND yst.SomeOtherColumn = 'somethingElse'
So now, the engine can go through all inventory items, to the corresponding details, to the actual deliveries without having to do individual searches each time which would be painful to trace / run / and performance.

SQL way to index across tables?

Is there a way to create a multi column index across tables?
For example, if I had the following tables:
Foo (Table Name)
FooID (PK)
BarID (FK)
FooName
Bar (Table Name)
BarID (PK)
BarName
I can do a
SELECT *
FROM Foo
LEFT JOIN Bar ON Foo.BarID = Bar.BarID
WHERE
FooName < "Bob"
AND BarName > "Smith";
In this case, I want a multi column index against Foo.FooName then Bar.BarName.
I did some research but wasn't able to find anything, perhaps I'm not using the right terms. My question may depend on the SQL engine, in which case I'm interested in MySQL specifically, but I am interested in any other engines as well.
Doing the multi column index on Foo with the Foreign Key doesn't help, as the underlying value of its Name is what I want for the speed.
Came across my own post years later and figured I could add some details, in case others have similar issues. As Mark B pointed out, each index is per table, however we can set things up that make this efficent, see below.
There are a couple of different things going on here, so can use indexes to help accomplish what we need. We need an index to help filter the main table, then an index that works well for the join and filter of the 2nd table. To help accomplish this, we can create the following 2 indexes:
CREATE INDEX idx_fooname ON Foo (FooName);
CREATE INDEX idx_barid_barname ON Bar (BarID, BarName);
Once those indexes are in place, a query can be used like:
SELECT *
FROM Foo USE INDEX(idx_fooname)
LEFT JOIN Bar USE INDEX (idx_barid_barname) ON Foo.BarID = Bar.BarID
WHERE
FooName < "Bob"
AND BarName > "Smith";
Smells like "over-normalization". Might it be worth moving those two fields into the same table?
Akiban was an Engine that could do cross-table JOINs, etc. But it no longer exists.
"Materialized Views" do not exist in MySQL (unless you implement them yourself).
As xQbert mentioned, you can use materialized views (or indexed views if you use Microsft SQL Server).
But first you have to change your LEFT JOIN to a INNER JOIN (because of materialized/indexed views can't handle outer joins. The left join does not seem to make sense in your query because of the WHERE ... BarName > "Smith".
That link might help you if you use SQL Server: https://www.simple-talk.com/sql/learn-sql-server/sql-server-indexed-views-the-basics/
After you created a materialized/indexed view, you can query the view directly (i'm not sure if the query optimizer will use it automatically).
Be aware that the materialized/indexed view will reduce your performance when you INSERT, DELETE or UPDATE into the used tables (what every index will do, too). The best idear is only to add the realy neccesary fields to the materialized/indexed view.

Recommended method to SELECT multiple columns within a nested subquery in mySQL

What methods are recommended for Selecting multiple columns within a nested subquery? It's been a while since I've coded any queries and I'm having some difficulty wrapping my head around this. The specific challenge is on Line 2 of the code below. The IN operand doesn't quite work here (see error message below), and I'm not sure if it's simply a matter of the syntax I'm using, and/or there is a much better way to go about this (i.e. using the HAVING operand or a JOIN statement)
SELECT * FROM Rules WHERE Rules.LNRule_id
IN(SELECT LNRule_id1,LNRule_id2,LNRule_id3,LNRule_id4 FROM Silhouette
WHERE Silhouette.Silhouette_Skirt=(SELECT Silhouette_Skirt FROM Style
WHERE Style.Style_Skirt='$Style_Skirt')
)
The purpose of this query is to SELECT all the relevant rows in table Rules for a particular value in table Style (i.e. $Style_Skirt), which it does by matching it to one of several factors - in this case the garment's Silhouette. What I am therefore trying to do in this portion of the query is SELECT all rows in table Rules who's ID (LNRule_id) matches values in any of the specified columns in table Silhouette
(SELECT LNRule_id1,LNRule_id2,LNRule_id3,LNRule_id4 FROM Silhouette WHERE Silhouette.Silhouette_Skirt=(...))
Edit
There is a many-to-many relationship (each Silhouette has several applicable Rules, and each Rule can apply to several Silhouettes). All the rules reside in the table 'Rules' (one per row), and each rule has an id ('LNRule_id'). The table 'Silhouette' has columns which tell it which rows need to be called from 'Rules' by 'LNRule_id' (LNRule_id1,2,3,4 indicate which Rules should be called, and store the values of the id's for the relevant rows in table 'Rules')
The error message currently being generated by the IN Operand is:
SQLSTATE[21000]: Cardinality violation: 1241 Operand should contain 1
column(s)
I think you want this query
SELECT * FROM Rules r
JOIN Silhouette s
(ON r.LNRule_id=s.LNRule_id1
OR r.LNRule_id=s.LNRule_id2
OR r.LNRule_id=s.LNRule_id3
OR r.LNRule_id=s.LNRule_id4)
JOIN Style st
ON s.Silhouette_Skirt=st.Silhouette_Skirt
WHERE st.Silhouette_Skirt = '$Style_Skirt'
mysql is complaining that on one side of IN you have a single column, and on the other side you have a multi-column rowset. In order for the IN operator to work, the rowset on the right side of IN must have the exact same number of columns as the left side; in this case, one column.
What you are trying to accomplish could perhaps be achieved if you did something like WHERE LNRule_id IN( SELECT LNRule_Id1 ...) OR LNRule_id IN( SELECT LNRule_Id2 ...) OR ... OR ... but the resulting query would be a monstrosity, and its performance would be horrendous. There may be other ways to go about it too, but anything you try will probably be similarly atrocious.
I do not have enough information to be absolutely sure about what I am saying, but it seems to me that the reason why you have this problem is that your database schema is not normalized. Generally, whenever you see a table with a group of columns having names that all begin with the same prefix and end with a number, it means that someone, somewhere, did not normalize their data.
To address the edit in your question, what you have implemented might conceptually be a many to many relationship, but as far as relational databases are concerned, (you know, the science, the theory, the established practices, the approaches necessary to get things to actually work,) it is definitely not a many to many relationship. Many to many relationships are most certainly not implemented with column1, column2, column3, ... columnN. To be sure that I am not making this stuff up, you can read what others say about many to many relationships here:
Many-to-many relations in RDBMS databases
So, my suggestion, if I correctly understand what is going on, would be to introduce a new table, called SilhouetteRules, which contains two columns, silhouette_id and rule_id. This table will implement a many-to-many relationship between silhouettes and your rules. Then of course you get rid of all the rule1, rule2, rule3, etc. columns from Silhouette.
Once you have done that, you can obtain all silhouettes and all rules associated with them using a query like this:
SELECT * FROM Silhouette
LEFT JOIN SilhouetteRules ON
Silhouette.id = SilhouetteRules.silhouette_id
LEFT JOIN Rules ON
SilhouetteRules.rule_id = Rules.id
The above query will yield multiple rows for each silhouette, where the silhouette fields will be identical from row to row, and only the rule fields will differ. Do not be surprised by this, that's how relational databases work.
Given a given_silhouette_id, you can retrieve all rules associated with it using a query like this:
SELECT * FROM Rules
LEFT JOIN SilhouetteRules ON
Rules.id = SilhouetteRules.rule_id
WHERE
SilhouetteRules.silhouette_id = given_silhouette_id
So, you are going to be using this query as a subquery in queries like the one in the question.
Now, regarding the query in the question, I am unable to tell you exactly how you would need to modify it to get it to work with the normalization that I proposed, because I cannot make sense of it. You see, even if you fix the problem that you currently have with SELECT * FROM table WHERE single-column IN multi-column-rowset, there is another problem further down: the WHERE Silhouette.Silhouette_Skirt=(SELECT ... part would not work either, because you cannot compare the value of a column against the result of a select statement. So, I do not know what you are trying to do there. Hopefully, once you normalize your schema and fix the first problem with your query, then the solution to the second problem will become obvious, or you can ask another question on stackoverflow.
P.S. did Mihai's answer work?

Shortcut for aliasing many columns on multiple joins?

My first mySQL project.
I am migrating a FileMaker DB to mySQL and having trouble with how to efficiently handle duplicate field (column) names from 3 left joined tables, combined with the fact that each table is supplying a large number of columns (50+). I understand the concept of aliasing columns. Is there a better way than to create several hundred alias lines to handle each column from each table? I've searched the site and not found a discussion of handling a large number of columns, which is common in FileMaker DBs...perhaps not in mySQL.
Current code is below, where I created the aliases for only ONE (WebPublish) of the ~50 fields for each of the 3 joined tables:
$query = "SELECT
Artwork.WebPublish as Artwork_WebPublish,
Artist.WebPublish as Artist_WebPublish,
Location.WebPublish as Location_WebPublish
FROM Review
LEFT JOIN Artwork ON Review._kf_ArtworkID = Artwork.__kp_ArtworkID
LEFT JOIN Artist ON Review._kf_ArtistID = Artist.__kp_ArtistID
LEFT JOIN Location ON Review._kf_LocationID = Location.__kp_LocationID
WHERE __kp_ReviewID = ?";
This query produces the desired response for one column from each joined table:
Array
(
[Artwork_WebPublish] => Yes
[Artist_WebPublish] => No
[Location_WebPublish] => Maybe
)
The question is whether I need to expand the aliases the long way to include 49 times more data.
Thanks for you help.
No, there's no SQL syntax for giving column aliases in a "batch" mode, for example applying the table name as a prefix to all columns (by the way, SQLite does support that feature by default).
One way to solve this is to refer to columns by ordinal position instead of by name, in whatever language you use to fetch the results.
Another solution is to define your tables with distinct column names so you avoid the name conflict. Some SQL identifiers, for example constraint names, are already required to be unique within the database they reside in, not only unique within a table. It may be a naming convention you want to use to apply the same rule to column names.

How to store data in mysql, to get the fastest performance?

I'm thinking about it, which of the following two query types would give me the fastest performance for a user messaging module inside my site:
The first one i thought about is a multi table setup, which has a connection table, and a main table. The connection table holds the connection between accounts, and the messaging table.
In this case a query would look like following, to get some data of the author, and the messages he has sent:
SELECT m.*, a.username
FROM messages AS m
LEFT JOIN connection_table
ON (message_id = m.id)
LEFT JOIN accounts AS a
ON (account_id = a.id)
WHERE m.id = '32341'
Inserting into it is a little bit more "complicated".
My other idea, and in my thought the better solution of this problem is that i store the data i would use in a connection table in the same table where is store the data of the mail. Sounds like i would get lots of duplicated entries, but no, because i have a field which has text type and holds user ids like this: *24*32*249*
If I want to query them, i use the mysql LIKE method. Deleting is an other problem, but for this i have one more field where i store who has deleted the post.
Sad about that i don't know how to join this.
So what would you recommend? Are there other ways?
Sounds like you are using an n:m relation.. if yes, don't put a list of ids in a single column but create a mapping table containing two columns - the primary key of table1 and the primary key of table2. Then selecting, inserting and deleting will all be easy and still cheap.
I wonder how many messages will be send to multiple recipients? It might just be easier to have it all in one table - MessageID, SentFrom, SentTo, Message, and dup it for multiple people. This obviously makes it extremely easy to query.
Definately avoid storing multiple ID's in one field and using LIKE - that'll be a performance killer - go with ThiefMasters suggestion if you want something like that.