I have two tables A & B, and B has a many:1 relationship with A.
When querying rows from A I'd also like to have corresponding B records returned as an array and added to the result array from A, so I end up with something like this:
A-ROW
field
field
B-ITEMS
item1
item2
item3
Is there a clean way to do this with one query (perhaps a join?), or should I just perform a second query of B on the id from A and add that to the result array?
It would be more efficient to join table B on table A. It will not give you the data in the shape you are looking for. But you can iterate over this result and build the data into the desired shape.
Here is some code to illustrate the idea :
// Join table B on table A through a foreign key
$sql = 'select a.id, a.x, b.y
from a
left join b on b.a_id=a.id
order by a.id';
// Execute query
$result = $this->db->query($sql)->result_array();
// Initialise desired result
$shaped_result = array();
// Loop through the SQL result creating the data in your desired shape
foreach ($result as $row)
{
// The primary key of A
$id = $row['id'];
// Add a new result row for A if we have not come across this key before
if (!array_key_exists($id, $shaped_result))
{
$shaped_result[$id] = array('id' => $id, 'x' => $row['x'], 'b_items' => array());
}
if ($row['y'] != null)
{
// Push B item onto sub array
$shaped_result[$id]['b_items'][] = $row['y'];
}
}
"... just perform a second query of B on the id from A and add that to the result array ..." -- that is the correct solution. SQL won't comprehend nested array structure.
To build on what Smandoli said--
Running the secondary query separately is more efficient because even if row data on the primary table (A) has changed, unchanged data on the secondary table (B) will result in a (MySQL) query cache hit assuming the IDs never change.
This is not necessarily true of the join query approach.
There will also be less data coming over the wire since the join approach will fetch duplicate data for the primary table (A) if the secondary table (B) has multiple rows associated with a single row in the primary table.
Hopefully anyone looking to do this (relatively) common type of data retrieval may find this useful.
Related
So, I have a table table with three columns I am interested in : value, entity and field_entity.
There are other columns that are not important for this question. There are many different types of entity, and some of them can have the same field_entity, but those two columns determine what the column value refers to (if it is an id number for a person or the address or some other thing)
If I need the name of a person I would do something like this:
select value from table where entity = 'person' and field_entity = 'person_name';
My problem is I need to get a lot of different values (names, last names, address, documents, etc.), so the way I am doing it now is using a left join like this:
select
doc_type.value as doc_type,
doc.value as doc,
status.value as status
from
table doc
-- Get doc type
left join table doc_type
on doc.entity = doc_type.entity
and doc.transaction_id = doc_type.transaction_id
and doc_type.field_entity = 'document_type'
-- Get Status
left join table status
on doc.entity = status.entity
and doc.transaction_id = status.transaction_id
and status.field_entity = 'status'
where doc.entity = 'users' and doc.field_entity = 'document' and doc.transaction_id = 11111;
There are 16 values or more, and this can get a bit bulky and difficult to maintain, so I was wondering if some of you can point out a better way to do this?
Thanks!
I assume that you are not in position to alter the table structure, but can you add views to the database? If so, you can create views based on the different types of entities in your table.
For example:
CREATE VIEW view_person AS
SELECT value AS name
FROM doc
WHERE doc.entity = 'person'
AND doc.field_entity = 'name';
Then you can write clearer queries:
SELECT view_person.name FROM view_person;
So I'm trying to update data from a temporary table into a main table.
Let's call these tables temp, and services.
The pseudocode would be something like this...
Sort temp by inserted_on
When service_id and location match in both tables:
If temp.column1 is not null, replace services.column1
If temp.column2 is not null, replace services.column2
etc...
I've got this bit working, although when I have multiple source rows in the temp table that match the condition, not all fields are being updated.
For example, I might have two rows with identical service_id and location, in one row column1 is null and column2 has a value, and in the next row the opposite is true. I need to update these one by one in the order they came in, and overwrite old data if necessary.
I also need to join the temp table inside the UPDATE to retrieve the keys I'm matching on.
I've tried the below code, but it only seems to be updating certain rows, and I can't quite figure out what the logic is behind it.
I'm not worried about the order, I'm just trying to figure out why it's leaving some blanks when there is data ready to fill the gaps.
UPDATE sloc
SET
sloc.ata = COALESCE(tmp.ata, sloc.ata),
sloc.atd = COALESCE(tmp.atd, sloc.atd),
sloc.atp = COALESCE(tmp.atp, sloc.atp),
sloc.eta = COALESCE(tmp.eta, sloc.eta),
sloc.etd = COALESCE(tmp.etd, sloc.etd),
sloc.etp = COALESCE(tmp.etp, sloc.etp),
sloc.plat = COALESCE(tmp.plat, sloc.plat),
sloc.plats_up = COALESCE(tmp.plats_up, sloc.plats_up),
sloc.cis_plats_up = COALESCE(tmp.cis_plats_up, sloc.cis_plats_up)
FROM
services_locations sloc
INNER JOIN services svc ON svc.id = sloc.sid
INNER JOIN ref_tiploc tloc ON tloc.id = sloc.tpl_id
INNER JOIN trainstatus_tmp tmp ON svc.rid = tmp.rid AND tloc.tpl = tmp.tpl
My goal is to create a query, macro or any solution that can do the task described below.
Lets say I have an access 2016 table named "student" with 12 records like this:
And then, lets say I have a second table named "matchme" with 4 records like this:
I need to find a way to
=> first, create a query that returns result of "graduation_date" are equal to Date "1/31/2017" from Table "student" .
=> second, from the result returned from first step, create a query that compare "email" from "student" table with "email" from "matchme" table, and return the [matched] record result.
So the desired result would be:
since the email gary#xxx.com and thomas#xxx.com exist in both tables.
How can I create a query like this?
you can download my access file from here: experiment.accdb
Simple:
select * from student
inner join matchme on student.email = matchme.email
where student.graduation_date = '1/31/2017'
Looking to your data sample you need a join on date and name between the two tables
select * from student
inner join matchme on student.graduation_date = matchme.graduation_date
and student.email = matchme.email
where student.graduation_date = '1/31/2017'
We have a reference data DB that is like an ODS/MDM but it's read only. The data is updated from the authoritative systems on various schedules. Every table maintains historic data - updates do an update existing & insert new, deletes do an update existing.
All tables are of the following form:
table <name>
surrogate key,
business key(s),
attribute(s),
effective_start_date,
effective_end_date
I want to expose 2 sets of views to users/systems for querying.
First view set is views that return only the current records from the respective table. That's easy.
Second view set should provide a way to query (by joining) multiple tables (all with history) and get the effective history of the result set.
For example, if a user issues something like the following query:
select
A.busines_key,
B.business_key,
effective_start_date(),
effective_end_date()
from
A inner join B on (A.b_fk_col = B.business_key)
then I need to transform this statement into:
select
A.busines_key,
B.business_key,
max( A.effective_start_date, B.effective_start_date ) effect_start_date,
min( A.effective_end_date, B.effective_end_date ) effective_end_date
from
A inner join B on (A.b_fk_col = B.business_key)
where
(A.effective_start_date between B.effective_start_date and B.effective_end_date
or
A.effective_end_date between B.effective_start_date and B.effective_end_date)
Really, what I need to be able to do is to add a step to the query plan right after the join(s):
e.g.
Instead of the original:
SELECT STATEMENT
MERGE JOIN CARTESIAN
BUFFER SORT
TABLE ACCESS BY INDEX ID A
INDEX FULL SCAN A_B_FK_IDX
BUFFER SORT
INDEX FULL SCAN B_PK_IDX
I could get something like:
SELECT STATEMENT
**** ADDED ****
EFFECTIVE RANGES // create/modify where & select clauses
TABLE ACCESS BY INDEX ID A // get the eff dates from A
TABLE ACCESS BY INDEX ID B // get the eff dates from B
****************
MERGE JOIN CARTESIAN
BUFFER SORT
TABLE ACCESS BY INDEX ID A
INDEX FULL SCAN A_B_FK_IDX
BUFFER SORT
INDEX FULL SCAN B_PK_IDX
Any thoughts on how I could do this? Thanks.
I'm using Bugzilla, and I essentially want to SELECT * FROM bugs table in the "bugs" database. However, the "assigned_to" column actually contains integer values (IDs) instead of a string with the user name.
These IDs match primary keys in the "profiles" table (the "userid" column), and the string I want my query to return is actually stored in the "realname" column in that table.
How can I modify this query to capture all columns in "bugs," but perform a lookup on the assigned_to column and return usernames?
SELECT b.*, p.realname FROM bugs b
JOIN profiles p
ON b.assigned_to = p.userid