I am looking join to do a left join on two tables. A string description on one table contains the UID of another table. I want to do the left join only if the description matches the UID. I need to return all records in the Transactions table.
Is it possible to use an IF or conditional statement to join the tables else leave the fields as null?
Here is a fiddle:
http://sqlfiddle.com/#!3/aaa38/5
Transactions table:
posted_date person_id description
2015-11-01 2 BZ#1414 Description 1414
2015-11-01 2 Another type of transaction with spaces in it
2015-11-01 3 BZ#1313 Another description 1313
2015-11-01 3 Another_description_without_spaces
Transaction_details table:
id person_id description
1414 2 additional stuff for 1414
1313 3 additional stuff for 1313
The results should look like:
posted_date person_id description id person_id description
2015-11-01 2 BZ#1414 Des... 1414 2 additional...
2015-11-01 2 Another typ... NULL NULL NULL
2015-11-01 3 BZ#1313 Ano... 1313 3 additional...
2015-11-01 3 Another_des... NULL NULL NULL
Joining like that really means that your Transactions table structure is not good (there should be a separate column for a field you could use to join to Transaction_details).
Having said that, you can still achieve what you want. You can join when the description starts with BZ# and then join on what follows, taking the length of id to extract the number. This is better than trying to find a space in the description as there may not be any spaces.
Left join will do the rest and fill the fields from tran_details with nulls if there was no match.
select *
from
transactions t
left join tran_details d
on t.person_id = d.person_id
and left(t.description, 3) = 'BZ#'
and substring(t.description, 4, len(cast(d.id as nvarchar(50)))) = d.id
I assumed that you want to join if BZ# is followed by the id from the detail table.
SQL Fiddle
The below query will work, as long as the first 3 chars are 'BZ#' in Transaction.Description column
select * from Transactions t left outer join Transaction_details td on t.person_id = td.person_id and left(t.description, 3) = 'BZ#' and
REPLACE(SUBSTRING(t.description, 0, CHARINDEX(' ',t.description, 0)), 'BZ#', '') = td.id
Related
I have two tables with one-to-one relations like: people has one to one relation with status table
**people table**
ID Name Status
1 Mick 1
2 Rohit null
3 Virat 1
4 Viru null
5 Gilly 2
6 Shann null
7 Mitch 3
**status table**
ID Status
1 started
2 not-started
3 pending
4 waiting
I need to get the people with status "null" and "started"(from ex: Mick, Rohit, Virat, Viru, Shann).
I tried with SQL query
select p.id, p.name
from people p
inner join status s on s.id = p.status
where (s.name IS NULL OR s.name = 'started')
this is giving only names which have a relation I mean "Mick, Virat" (skips nulls).
I don't know what I am missing here. thanks in advance
In order to keep people records where no status matches you need an OUTER join. In this case, a LEFT OUTER JOIN, which is often shortened to just LEFT JOIN:
select p.id, p.name
from people p
left join status s on s.id = p.status
where coalesce(s.name, 'started') = 'started'
I have stored data into several MySQL 5.x tables in order to normalize, now I am struggling on how to retrieve this data in one line per dataset.
E.g.
Table 1: articles, holding also 2 values in this example per article
article_id | make | model
1 Audi A3
Table 2: article_attributes, where one article can have several attributes
article_id | attr_id
1 1
1 2
2 1
Table 3: article_attribute_names
attr_id | name
1 Turbo
2 Airbag
Now I want to retrieve it, with one line per dataset
e.g.
SELECT a.*, attr_n.name AS function
FROM `articles` a
LEFT JOIN article_attributes AS attr ON a.article_id = attr.article_id
LEFT JOIN article_attribute_names AS attr_n ON attr_n.attr_id = attr.attr_id
-- group by attr.article_id
This will gives me:
article_id | Make | Model | function
1 Audi A3 Turbo
1 Audi A3 Airbag
But I am looking for something like this:
article_id | Make | Model | function1 | function2
1 Audi A3 Turbo Airbag
Is this even possible, and if yes, how?
The simplest method is to put the values into a delimited field using group_concat():
SELECT a.*, GROUP_CONCAT(an.name) AS functions
FROM articles a LEFT JOIN
article_attributes aa
ON a.article_id = aa.article_id LEFT JOIN
article_attribute_names aan
ON aan.attr_id = aa.attr_id
GROUP BY a.article_id;
Aggregating by article_id is okay, assuming that the id is unique (or equivalently declared as a primary key).
If you actually want the results in separate columns, that is more challenging. If you know there are at most two (as in your example), just use aggregation:
SELECT a.*, MIN(an.name) AS function1,
(CASE WHEN MIN(an.name) <> MAX(an.name)
THEN MAX(an.name)
END) as function2
FROM articles a LEFT JOIN
article_attributes aa
ON a.article_id = aa.article_id LEFT JOIN
article_attribute_names aan
ON aan.attr_id = aa.attr_id
GROUP BY a.article_id;
I try doing somethin like this:
Table A
NameID | name | other rows
1 | name1
2 | nameA
3 | nameX
Table B
UniqueID | NameID | other rows
1 | 1
2 | 2
3 | 6
4 | 17
5 | 22
No I want to do a select like this:
SELECT tablea.ID, tablea.name, tableb.*
FROM tablea
LEFT JOIN tableb ON tablea.ID = tableb.ID
WHERE
tablea.ID != '0'
I search now for the ID and the name.
I get allways the name, but not allways the ID, only if the ID is in table B also. why?
My result is like this:
ID | name
1 | name1
2 | nameA
NULL | nameX
I have this situation:
I have in TABLE A films (with names, datas, etc.). In table B I have
ratings of these movies. Now I search with a form and want to see all movies, that have an incomplete rating, or no ratings at all, or any film despite of it has or not ratings.
With a LEFT JOIN on TABLE A (all movies) and with the addition ON-Condition I search if the movie has a rating or not (is or is not in TABLE B).
My result get me the name of the movie, allways. But if the movieID is not in table B (= no ratings recorded), I do not get the ID of the Movie in table A in the result.
So not my entire result is NULL only the movieID, I can pull any other DATA out of TABLE A for this movie, but not the ID.
so why?
Is it not possible to ask for the tablea.ID, if the ID is in the ON - term?
Left join will show all rows form the first table and add data from the second table when a matching rows is found.
If no matching row is found, left join will return null (which means "unknown") for those rows.
If you want to discard rows from you result that have no matching row in the other table, you have to use "inner join" instead of "left join".
Check visual representation of joins for more info: https://www.codeproject.com/Articles/33052/Visual-Representation-of-SQL-Joins
The solution is, to set an alias for the row in table one, which is duplicated in the second table and has due to no "rows" in second table the outcome NULL.
If I put an alias into it, I can select it and LEFT JOIN will do the rest.
What to know: mysql_fetch_object will always select the last mention of a row, if the row name is identic in other tables of the select. In my case I LEFT JOIN a second table with the same row-name. Due to LEFT JOIN I have no results on the ON condition and the correct outcome for my query is NULL.
I currently have 2 tables, vaccination and vaccination_type which can be joined using vaccine_id attribute.
vaccination is basically the joining many-to-many table between vaccination_type and patient. It has 3 attributes: vaccine_id, registration_no and date. Vaccination_type has vaccine_id, vaccine_name.
I want to join these 2 tables and get these entries:
vaccine_id vaccine_name registration_no date
1 influenza 1111 2015-01-15
2 hepatitis B null null
3 polio 1111 2015-01-15
4 hepatitis A 1112 2015-01-15
This means that even the patient has not done hepatitis B vaccination, I still want the entry to be null. But I only want the one to specific registration_no, in this case 1111, in other words I also want the hepatitis A is written as null, as it is not done by 1111. So my expected result if I am dealing with 1111 will be:
vaccine_id vaccine_name registration_no date
1 influenza 1111 2015-01-15
2 hepatitis B null null
3 polio 1111 2015-01-15
4 hepatitis A null null
I have tried using all of the join, but cannot find the one that works perfectly. Any suggestion will be appreciated, thanks!
If you want the information for one registration, then that should be your base table. Then you want to get all vaccination types for that registration. Finally join onto the vaccination table to get the date it was administered, or NULL if that person never got it:
SELECT
registrations.registration_no,
vaccination_type.vaccine_id,
vaccination_type.name,
vaccination.date
FROM
registrations
CROSS JOIN vaccination_type
LEFT JOIN vaccination ON registrations.registration_no = vaccination.registration_no AND vaccination_type.vaccine_id = vaccination.vaccine_id
WHERE
registrations.registration_no = 1111
Fiddle: http://sqlfiddle.com/#!2/e8b2d/5
Use LEFT JOIN it join both tables.
Try this:
SELECT VT.vaccine_id, VT.vaccine_name, V.registration_no
FROM Vaccination_type VT
LEFT JOIN vaccination V ON VT.vaccine_id = V.vaccine_id;
As I now understand the question, you need a listing of all vaccinations against which you will need to see all patients (or a particular patient) to see which vaccinations a patient has and has not taken. Yes, that is possible, but it requires all three tables. The Vaccination_Type table has the list of all vaccinations, the Patients table has the list of all patients. The Vaccinations table has the list of vaccinations that patients have taken.
The first step is to create a Cartesian product of all vaccinations with all patients. This will give you a list of all vaccinations and for each vaccination a list of all patients.
select vt.vaccine_id, p.registration_no
from Vaccination_Type vt
cross join Patients p;
Then left join with the existing cross table.
select vt.vaccine_id, p.registration_no, v.vaccination_date
from Vaccination_Type vt
cross join Patients p
left join Vaccinations v
on v.vaccine_id = vt.vaccine_id
and v.registration_no = p.registration_no;
This gives you all vaccinations with all patients. If the patient has received the vaccination, it will be followed by the date of that vaccination. If not, the date field will be NULL.
If this is something you need periodically, like for reporting, you can create a view of the above query and just select from the view.
If you are only interested in seeing this listing in regards to a particular patient, you can select from the view:
select vaccine_id, registration_no, vaccination_date
from All_Vaccinations
where registration_no = 1111;
However, if a particular patient's vaccination history is mostly what you want, you can obtain that with just two tables with a little better performance:
select vt.vaccine_id, v.vaccination_date
from Vaccination_Type vt
left join Vaccinations v
on v.vaccine_id = vt.vaccine_id
and v.registration_no = 1111;
Notice that registration_no is left out of the select list as we are no longer using the Patients table. We could have used the registration_no field from the Vaccinations table but then if the patient is missing a vaccination, not only the date field but also the registration_no field of the result set would contain NULL. There is no reason to expose a field that A) you already know the value of and B) will have the proper value in some rows and NULL in others. That could lead to some confusion. When you are looking at the data for more than one patient at a time, you must use the three-table join as the registration_no would be required to make sense of the output.
I have the following table schema:
tbl_portfolio
----------
id (auto number)
name
-
tbl_registration
----------------
id(auto number)
name
portfolio_id (FK to tbl_portfolio.id)
-
tbl_fund
---------
id (auto number)
registration_id (FK to tbl_registration.id)
-
tbl_transaction
---------------
id (auto number)
fund_id (FK to tbl_fund.id)
type
shares
price
I need to create a query that in psuedo-code would do the following:
SELECT port.*, SUM ALL transactions for each portfolio,
FROM tbl_portfolio port
INNER JOIN tbl_registration reg ON reg.portfolio_id = port.id
LEFT JOIN tbl_fund fund on fund.registration_id = reg.id
LEFT JOIN tbl_transaction trans ON trans.fund_id = fund.id
Now of course that query won't work...What I am needing essentially is to sum all the Price * Units for each fund, and then sum those together for each registration, and then sum all of that together for each portfolio.
Each portfolio can have multiple registrations, and each registration can have multiple funds, and each fund can have multiple transactions.
The last item that is throwing a stickler in this, there may be 10's or 100's of portfolios to count so I have no idea how to write the query, much less write it in an effective way that is not relying on subqueries that would cause it to have severely poor performance.
Thank you for the help!
Edit:
PinnyM's answer works and queries the data correctly - however I should expand on the full need.
Besides the tbl_transaction there is also a tbl_distri and tbl_div. Both have fund_id as FK to tbl_fund.id . I need to get the SUM's of tbl_distri.amount and tbl_div.units.
So the full psuedo query would be something to the effect of:
SELECT port.*, SUM ALL transactions for each portfolio, SUM(div.units), SUM(distri.amount)
FROM tbl_portfolio port
INNER JOIN tbl_registration reg ON reg.portfolio_id = port.id
LEFT JOIN tbl_fund fund on fund.registration_id = reg.id
LEFT JOIN tbl_transaction trans ON trans.fund_id = fund.id
LEFT JOIN tbl_distri distri on distri.fund_id = fund.id
LEFT JOIN tbl_div div on div.fund_id = fund.id
Have you tried using SUM()?
SELECT port.*, SUM(trans.shares * trans.price) AS transaction_totals
FROM tbl_portfolio port
INNER JOIN tbl_registration reg ON reg.portfolio_id = port.id
LEFT JOIN tbl_fund fund on fund.registration_id = reg.id
LEFT JOIN tbl_transaction trans ON trans.fund_id = fund.id
GROUP BY port.id
Judging from your question, you are looking for a rolled-up SUM
SELECT port.id AS port_id,
reg.id AS reg_id,
fund.id AS fund_id,
SUM ( trans.shares * trans.price) AS net_asset_value
FROM tbl_portfolio port
INNER JOIN tbl_registration reg ON reg.portfolio_id = port.id
LEFT JOIN tbl_fund fund on fund.registration_id = reg.id
LEFT JOIN tbl_transaction trans ON trans.fund_id = fund.id
GROUP BY port.id, reg.id, fund.id WITH ROLLUP
This will give you the sums id by id. You can use other JOIN operations with this as a subquery to fetch the textual names.
This will give results like this:
port_id reg_id fund_id net_asset_value
1 1 1 150.00
1 1 2 100.00
1 1 NULL 250.00 (rollup of previous two lines)
1 2 1 24.00
1 2 4 80.00
1 2 NULL 104.00 (rollup of previous two lines)
1 NULL NULL 354.00 (rollup at portfolio level)
3 1 1 40.00
3 1 2 50.00
3 1 NULL 90.00 (rollup of previous two lines)
3 2 1 14.00
3 2 4 60.00
3 2 NULL 74.00 (rollup of previous two lines)
3 NULL NULL 164.00 (rollup at portfolio level)
NULL NULL NULL 518.00 (grand total)
The NULLs make it into this resultset because that's what WITH ROLLUP does. This resultset only has the IDs in it; presumably the IDs are unique even if the names aren't. Non-unique names for portfolios, funds, etc, will mess up the GROUP BY pretty badly. Hence my earlier comment about retrieving the names.