I'm trying to get the content of 3 different tables.
table A = Is containing our users list, table B = Is returning contracts related to users,table C = Is returning formula details related to the contracts.
In order to make it the right way, I'm using the following multi table request:
SELECT * FROM rscm_students A, rscm_files B, rscm_formulas C
WHERE B.dossier_status = 0
AND A.student_agency = :agency
AND B.file_student_id = A.id
AND B.file_formula_id = C.id
AND C.formula_place = 0
GROUP BY A.student_uniqid
ORDER BY B.file_date_create";
This is where the whole damn thing become a little complicated. It is returning the correct datas, but as the primary key of every table here is called "id". I can't do some foreach in php. If I got 3 contracts on 1 user, it impossible for me to regroup every contract in the same user array.
I'm still not an expert in SQL, that's why I'm using Phinx to control my database. This is also why my primary keys are named "id".
If you have a good idea, please let me know!
Alright, I will make an answer out of it.
First off, don't use
select *
The above select is fine for quick and dirty development prior to production. But it makes a mess out of things such as your joins with common column names coming out of multiple tables (like id and others).
Use modern explicit join syntax. Don't use the older join style. So use join and on.
Lastly with table aliases, create unique output column names for the id columns or other clashes such as
A.id as aid, B.id as bid
Related
I'm working with a database that is not normalized efficiently so I am having to get creative in joining tables. I have one table that has a column name 'direction_or' with full names like 'Northwest','Southwest' and another table with the same column name but abbreviates them like 'NW','SW' and I am needing to join the tables on those parameters. Joining on keys is not possible unfortunately.
I was wondering if it is possible to use a CASE WHEN or something to evaluate the full name and then make it the abbreviated name then JOINING the tables based on that abbreviated name in one query.
Like I said I'm having to get creative and this has got me stumped.
Something like this, forgive me for my ignorance.
SELECT * FROM user.directions AS A (somehow change the value of A.direction_or AS newAbb) LEFT JOIN stored.directons AS B ON A.newAbb = B.direction_or
Thank you in advance.
If you want an easy yet heavy way, you can :
create a table pairing terms. It should be pretty lightweight and most probably cached everytime you use it, but it still have a weight since imbriqued select.
You would then do :
SELECT * FROM user.directions AS A LEFT JOIN stored.directons AS B ON (select desiredvalue from StatesAndAbreviation where state = A.newAbb) = B.direction_or
or something like this.
And other way to work around this would be to use enums. By creating two enums and mapping corresponding strings to equivalent integers, you could probably compare them quite easily. You need to alter the tables to change the types of the varchar -> corresponding enum.
See:
https://dev.mysql.com/doc/refman/8.0/en/enum.html
Edit: When comparing enums, you would probably have to do something awkward like where A.enumvar+0 = B.enumvar+0. You can still try without it though since the code would be easier to read if it works.
You can create a lookup table which maps full names to abbreviated names, then join with that table in your query.
Else you can also do it this way:
SELECT * FROM
(
SELECT *,
CASE
WHEN direction_or = 'Northwest' THEN 'NW'
WHEN direction_or = 'Southwest' THEN 'SW'
-- all other possibilities here
ELSE ''
END as direction_or_abb
FROM user.directions
) a
JOIN stored.directions b
ON a.direction_or_abb = b.direction_or
I wonder if any can help me understand something I'm trying to solve.
I'm working on a wordpress site but this is more a sql question as I'm just querying to get some results within a template file.
I have a gallery of pictures which are advert boxes, and I need to pull these in relation to a supplied movie name, to do this Im using some custom fields on the ad pic called 'adlink' (link off ad) and ad
I'm using the nextgen gallery plugin and querying those tables, and I have three tables in total that contain the data I need to query.
ngg_pictures, nggcf_field_values & nggcf_fields.
the nggcf tables are custom fields tables,
I have got so far I can get what I need in two seperate queries, but I can't combine these into one query as it means querying the nggcf_field_values table twice, which I can't seem to sort.
I have hardcoded the search criteria in for the mo, but the 'close-encounters' bit would be a passed var, and the '156' would be the pid from the first query.
SELECT `eg_ngg_pictures`.`filename`, `eg_nggcf_field_values`.`fid`, `eg_nggcf_field_values`.`pid`
FROM eg_ngg_pictures, eg_nggcf_field_values
WHERE ((`eg_nggcf_field_values`.`field_value` LIKE 'close-encounters') AND (`eg_nggcf_field_values`.`pid` = eg_ngg_pictures.pid))
SELECT `eg_nggcf_field_values`.`field_value`
FROM eg_nggcf_field_values, eg_nggcf_fields
WHERE ((`eg_nggcf_fields`.`field_name` = 'adlink') AND (`eg_nggcf_fields`.`id` = eg_nggcf_field_values.fid) AND (`eg_nggcf_field_values`.`pid` = '156'))
any help would be greatly appreciated, I can get the results with what I have, but I like to understand how to combine these two and write better SQl. Thanks MRO
After looking at the Wordpress extension, I think the eg_nggcf_fields is the table that contains the name for a custom field. The eg_nggcf_field_values table contains the values of that custom field per picture.
So if you're looking for two fields called moviename and adlink, you have to look up two rows in the field_values table. You can join a table twice if you give it a different alias:
select pic.filename
, pic.pid
, fv1.field_value as MovieName
, fv2.field_value as Adlink
from eg_ngg_pictures pic
inner join -- Find ID for the field called 'moviename'
eg_nggcf_fields f1
on f1.field_name = 'moviename'
inner join -- Find value for field moviename for this picture
eg_nggcf_field_values as fv1
on fv1.pid = pic.pid
and fv1.fid = f1.fid
inner join -- Find ID for the field called 'adlink'
eg_nggcf_fields f2
on f2.field_name = 'adlink'
inner join -- Find value for field adlink for this picture
eg_nggcf_field_values as fv2
on fv2.pid = pic.pid
and fv2.fid = f2.fid
where fv1.field_value like 'close-encounters'
First of all, I'd recommend sticking to modern ANSI syntax for JOINing tables, which means using the JOIN clause.
Instead of using:
FROM table1, table2 WHERE table1.id = table2.pid
use:
FROM Table 1 JOIN table2 ON table1.id = table2.id
For simplicity's sake, I'd also recommend you to alias tables, as that tends to make the code more readable. Instead of having to write out egg_ngg_pictures every time, you can simply refer to the alias you assign it instead.
Lastly, when you use a LIKE operator, you usually add a wild-card character (typically %. I.e. LIKE '%123' or LIKE '123%'). You seem to look only for complete matches, which means you can just stick to using =, as that should give you slightly better performance.
Now to rewrite your query, I'd use something like the following:
SELECT
pic.filename
, fieldval.fid
, fieldval.pid
, fieldval.field_value
FROM
eg_ngg_pictures pic
JOIN eg_nggcf_field_values fieldval ON fieldval.pid = pic.pid
JOIN eg_nggcf_fields fields ON fields.id = fieldval.fid
WHERE
((fieldval.field_value = 'close-encounters')
AND fields.field_name = 'ad_link'
Note that I am not able to test the query, as I do not have your schema. But by incorporating the two queries into a single query, the join on the field_Values.PID retreieved with the 'close_encounters' value should already exist.
If the query does not work, feel free to create a SQL fiddle with the relevant tables and some data, and I'll try and get it to work with that.
SELECT accounts.NameSurname, Projects.Name, personnels.NameSurname
FROM accounts
JOIN projects ON ( accounts.Id = projects.AccountId )
JOIN projectpersonnels ON ( projects.Id = projectpersonnels.ProjectId )
JOIN accounts AS personnels ON ( projectpersonnels.AccountId = personnels.Id )
results in NameSurname Name NameSurname colums
Why would the queries above and below result in different "columns" titles and number of columns?
I am running the query above in phpmyadmin.
using mysql and codeigniter 1.7.3
$this->db->select('accounts.NameSurname,projects.Name,personnels.NameSurname');
$this->db->from('accounts');
$this->db->join('projects','accounts.Id = projects.AccountId' );
$this->db->join('projectpersonnels','projects.Id = projectpersonnels.ProjectId');
$this->db->join('accounts as personnels','projectpersonnels.AccountId = personnels.Id');
$q=$this->db->get();
results in: NameSurname (which is personnel) Name (which is project)
Thank you for reading and replying.
It has to do with how CI creates results. You have a name collision. Both of the NameSurnames are trying to be assigned to the same "key" in your object/array result. Since personnels.NameSurname is second, it is overwriting the value of accounts.NameSurname (because it is being assigned to the same key). If you were to use accounts.NameSurname as aNameSurname (not 100% sure if that is correct syntax for MySQL) instead of accounts.NameSurname that would likely make your results consistent.
This would actually cause the same problem with the PDO classes in certain circumstances too: FETCH_ASSOC and FETCH_OBJ would both only show two columns, but FETCH_ARRAY would show three. FETCH_BOTH would result in there being three numeric indexes and two associative indexes. (Not that you asked about PDO's, but I thought it might further illustrate the point).
I have quite a problem concerning the use of relational database concepts in Delphi 2009 with MyDAC.
I have a database structure that looks somehow like the following:
Item
id
name
Storage
id
name
StorageItem
id
item_id
storage_id
place
Now when I have an active dataset from "Item" how can I display all associated Storages in for example a DBGrid?
By the way: Would it be better to not use "id" in every table but to alter it and use something like for example "id_item" or "id_storage"?
Thank you in advance :)
With StorageItem you created a
many-to-many relationship. If you
need just one-to-many (many storages
are related to one item, but you
don't need the vice versa), then you
may just add another field to the
Storage table (item_id) that would
be a foreign key for Items table.
Then you create an index on
item_id in Storage table, and
connect the two tables in
master-detail relationship.
If you do need many-to-many then you
may add a query component with SQL
(select * from StorageItem where
item_id := :current_storage_id), and
current_storage_id is your query's
parameter.
Select a.ID, b.Name, a.Place
from StorageItem a
inner join Storage b
on (a.id = b.id)
the above query will return all the items in StorageItem table with it's name, now if you want to filter it to return only items for a specific item add where clause to be like
Select a.ID, b.Name, a.Place
from StorageItem a
inner join Storage b
on (a.id = b.id)
where a.item_id = 1 -- place the item id here
you can use where with parameters such as:
MyQuery.Sql.Text := ' Select a.ID, b.Name, a.Place from StorageItem a
+ ' inner join Storage b on (a.id = b.id) '
+ ' where a.item_id = :ItemNo ';
MyQuery.ParamByName('ItemNo').asInteger := 1;
MyQuery.Open;
and assign the query above to dbGrid
also you can use MasterSource property to make the relations without using the "where" part
I'm not familiar with MyDAC personally, but most dataset components have some way to establish master-detail relationships. Check if there's a MasterSource property on your dataset, or some similar way to link a detail dataset to a master dataset. If not, you could use a TDatasetField to establish a link, and filter the nested dataset to only display the right records.
As for ID column names, it's a good idea to give a descriptive name to each field, so you can tell by looking at the code that you've got your links right. If you call your id column "id", that could be any id column, and that could get confusing if you start passing around references to datasets. But if it's called item_id every time, (not item_id sometimes and id_item sometimes) then you always know exactly what you're looking at. It makes it easier to know that your code is right, too. A filter that says "master.item_id = detail.item_id" is easier to read that "master.id = detail.item_id". That could be wrong and fail silently if master is assigned to the wrong dataset, for example.
MySQL setup: step by step.
programs -> linked to --> speakers (by program_id)
At this point, it's easy for me to query all the data:
SELECT *
FROM programs
JOIN speakers on programs.program_id = speakers.program_id
Nice and easy.
The trick for me is this. My speakers table is also linked to a third table, "books." So in the "speakers" table, I have "book_id" and in the "books" table, the book_id is linked to a name.
I've tried this (including a WHERE you'll notice):
SELECT *
FROM programs
JOIN speakers on programs.program_id = speakers.program_id
JOIN books on speakers.book_id = books.book_id
WHERE programs.category_id = 1
LIMIT 5
No results.
My questions:
What am I doing wrong?
What's the most efficient way to make this query?
Basically, I want to get back all the programs data and the books data, but instead of the book_id, I need it to come back as the book name (from the 3rd table).
Thanks in advance for your help.
UPDATE:
(rather than opening a brand new question)
The left join worked for me. However, I have a new problem. Multiple books can be assigned to a single speaker.
Using the left join, returns two rows!! What do I need to add to return only a single row, but separate the two books.
is there any chance that the books table doesn't have any matching columns for speakers.book_id?
Try using a left join which will still return the program/speaker combinations, even if there are no matches in books.
SELECT *
FROM programs
JOIN speakers on programs.program_id = speakers.program_id
LEFT JOIN books on speakers.book_id = books.book_id
WHERE programs.category_id = 1
LIMIT 5
Btw, could you post the table schemas for all tables involved, and exactly what output (or reasonable representation) you'd expect to get?
Edit: Response to op author comment
you can use group by and group_concat to put all the books on one row.
e.g.
SELECT speakers.speaker_id,
speakers.speaker_name,
programs.program_id,
programs.program_name,
group_concat(books.book_name)
FROM programs
JOIN speakers on programs.program_id = speakers.program_id
LEFT JOIN books on speakers.book_id = books.book_id
WHERE programs.category_id = 1
GROUP BY speakers.id
LIMIT 5
Note: since I don't know the exact column names, these may be off
That's typically efficient. There is some kind of assumption you are making that isn't true. Do your speakers have books assigned? If they don't that last JOIN should be a LEFT JOIN.
This kind of query is typically pretty efficient, since you almost certainly have primary keys as indexes. The main issue would be whether your indexes are covering (which is more likely to occur if you don't use SELECT *, but instead select only the columns you need).