How to concatenate the output of a SQL query into one string - mysql

I am newbie in writing SQL queries and this is for a mySQL database.
I have a Table called PatientVisit (PV), which has a one to one with BillMaster (BM). Each visit has one bill, which then has a one to many with BillDetail (BD). When i list out the Visit details from PatientVisit (PV), i need to print a string with the set of 'ServiceName' columns associated with that one visit.
So for example, the PatientVisit.ID number '1' has a corresponding BillMaster.Bill No '1' which has 2 entries in BillDetail 'Consultation' and 'Dressing'.
When i print details of Visit 1, i need 'Consultation,Dressing' as one string value for the 'Service Name' column.
If i had a one to one , then the query would have been simple as follow :
select PV.ID, BM.BillNo, BD.ServiceName
FROM PatientVisits PV INNER JOIN BillMaster BM ON BM.VisitID = PV.ID
INNER JOIN BillDetail BD ON BD.BillNo = BM.BillNo
WHERE ....
However, since it is one to many for the ServiceName column, how can this query be written ?
Thanks,
Chak.

Try this
select PV.ID, BM.BillNo,
GROUP_CONCAT(BD.ServiceName)
FROM PatientVisits PV INNER JOIN BillMaster BM ON BM.VisitID = PV.ID
INNER JOIN BillDetail BD ON BD.BillNo = BM.BillNo
WHERE ..
GROUP BY PV.ID,BM.BillNo
..

Related

Migrating data from one database to another using LEFT JOIN

Let me start by saying I am relatively new to MySQL. I am trying to migrate data from one database to another. Let us call one database DB1 and the other DB2.
DB2 has tables the following tables.
Patient: id, person_id and start_regimen_id
Regimen: id, code
Visit: id, patient_id, regimen_id,next_appointment_date
DB1 has the following tables:
Patient: id,medical_record_number,current_regimen, start_regimen nextappointment
Now:
regimen_id data should be inserted to current_regimen
start_regimen_id data should be inserted to start_regimen
next_appointment_date should be inserted to nextappointment
This is what I have now:
SELECT
p.person_id AS medical_record_number,
r.code AS start_regimen,
??? AS current_regimen,
DATE_FORMAT(v.next_appointment_date, '%Y-%m-%d') AS
nextappointment,
FROM patient p
LEFT JOIN regimen r ON r.id = p.start_regimen_id
LEFT JOIN (
SELECT patient_id, MAX(next_appointment_date) as next_appointment_date
FROM visit
WHERE next_appointment_date IS NOT NULL
GROUP BY patient_id
) v ON v.patient_id = p.id
I have remained to migrate regimen_id (visit) on DB2 to current_regimen (patient) on DB1. I don't know how to use two LEFT JOIN to get data from two tables for one table.
Any assistance will be greatly appreciated because I am really stuck.
Seems like we would want to include regimen_id in the GROUP BY from the visit table, and then match that to id from the regimen table. Given the outer join, it appears that patient may not have any regimen associated, so I would include matching of NULL values of regimen_id.
LEFT
JOIN ( SELECT vv.patient_id
, vv.regimen_id
, MAX(vv.next_appointment_date) AS next_appointment_date
FROM visit vv
WHERE vv.next_appointment_date IS NOT NULL
GROUP
BY vv.patient_id
, vv.regimen_id
) v
ON v.patient_id = p.id
AND v.regimen_id <=> r.id
But that's just a guess. Without a specification (preferably illustrated by example data and and expected output) we're just guessing.
Note:
foo <=> bar
is a NULL-safe comparison, a shorthand equivalent to
( foo = bar OR ( foo IS NULL AND bar IS NULL ) )

SQL query to select from 3-4 tables

Please find below a link to the table-structure I have set up and to the query I am running.
Link to tables and query.
The present result is that only the firstnames, lastnames and "education_finished" are showing. But all the option_id's and their related lang_values still show "NULL".
The desired result:
Any suggestions how to solve?
Below is the query that you are using:
SELECT d.pf_firstname, d.pf_lastname, f.field_id, fl.option_id,
d.pf_education_finished, fl.lang_value
FROM phpbb_profile_fields_data d
LEFT JOIN phpbb_profile_fields f
ON d.pf_education_finished = f.field_name
LEFT JOIN phpbb_profile_fields_lang fl
ON f.field_id = fl.field_id
ORDER BY d.pf_lastname ASC
The reason why you are getting null value is because of this condition:
LEFT JOIN phpbb_profile_fields f
ON d.pf_education_finished = f.field_name
You are trying to join on pf_education_finished (int) field of one table and field_name (int) field of another table. Also, there are no matching values (e.g. pf_education_finished contains numbers whereas field_nameis 'education finished').
If you want the query to return something then you need to join on field_id and phpbb_profile_fields needs to have some records with matching field id, e.g.:
SELECT d.pf_firstname, d.pf_lastname, f.field_id, fl.option_id,
d.pf_education_finished, fl.lang_value
FROM phpbb_profile_fields_data d
LEFT JOIN phpbb_profile_fields f
ON d.pf_education_finished = f.field_id
LEFT JOIN phpbb_profile_fields_lang fl
ON f.field_id = fl.field_id
ORDER BY d.pf_lastname ASC
Here's the updated SQL Fiddle.
SELECT d.pf_firstname, d.pf_lastname, f.field_id, fl.option_id,
d.pf_education_finished, fl.lang_value
FROM phpbb_profile_fields_lang fl
inner JOIN phpbb_profile_fields f
ON f.field_id = fl.field_id
inner JOIN phpbb_profile_fields_data d
ON f.field_id = fl.field_id
ORDER BY d.pf_lastname ASC
This is the optional query if you want to display data from 3-4 tables but in this query names are repeats as per the count of field_id present in phpbb_profile_fields_lang.
The exact solution you are looking is, when you have the same primary key in all the tables from which you are retrieving the data.
Thank you.

Select rows based on an array of values and join data

I have a MySQL database of contacts with three tables.
person
personContact
personDetails
Each contact shares a primary key called 'ID'
The personContact table contains a value called 'personZip' which happens to be their mailing address zip code.
I'd like to write a SQL query that will give me all the contact data for each person in a specific array of zip codes.
I've written a simple statement to perform an inner join on 2 of the tables:
SELECT * FROM `personContact`
INNER JOIN person
ON personContact.ID=person.ID
I've written a statement to select only the zip codes I need:
SELECT * FROM 'personContact'
WHERE personContact.personZip=12564
OR personContact.personZip=12563
OR personContact.personZip=12522
OR personContact.personZip=12590
OR personContact.personZip=12594
OR personContact.personZip=12533
OR personContact.personZip=12570
OR personContact.personZip=12589
OR personContact.personZip=10509
I'm not sure how to perform two joins, to merge all columns from all three tables.
I'm not sure how to write the query to accommodate both the selection of zip codes and the JOINS.
MySQL errors are not helping me move in the right direction.
You were almost there. Use in which is equivalent to or.
SELECT pc.* --select columns from the other tables as needed.
FROM
`personContact` pc
INNER JOIN person p ON pc.ID = p.ID
INNER JOIN personDetails pd on pd.ID = p.ID
where pc.personzip in (12563, 12522, 10509) -- add more zips as needed
Join all three tables and return all columns while filtering by zip code:
SELECT person.*, personContact.*, personDetails.*
FROM person
INNER JOIN personContact ON personContact.ID = person.ID
INNER JOIN personDetails ON personDetails.ID = person.ID
WHERE personContact.personZip = 12564
OR personContact.personZip = 12563
OR personContact.personZip = 12522
OR personContact.personZip = 12590
OR personContact.personZip = 12594
OR personContact.personZip = 12533
OR personContact.personZip = 12570
OR personContact.personZip = 12589
OR personContact.personZip = 10509
EDIT: Multiple OR statements can be replaced using IN as others have suggested
WHERE personContact.personZip IN (12564, 12563, 12522, 12590 ...)

Ms Access query not working. Please suggest

Help needed for the below query. Thanks
SELECT b.SchemeCode_Db,
'DBELE',
1,
SecurityCode_Db,
"DIRECT",
Qty,
Price,
((Commission + TransferCharge) / Qty) AS Charges,
"",
iif(Buy_sell_code = "1110",
((Qty * Price) + Commission + TransferCharge),
iif(Buy_sell_code = "1120", ((Qty * Price) - Commission
- TransferCharge
))) AS totalCost,
BrokerCode_Db,
"",
Deal,
Format(Tradedate, "dd/MM/yyyy"),
Format(Valuedate, "dd/MM/yyyy"),
'', 'BSE', 'CH', 'D', '',
iif(Buy_sell_code = "1110",
'PUR',
iif(Buy_sell_code = "1120", 'SAL')) AS txn,
'USD' AS cur
FROM tbl_EQUITYINPUT a
LEFT JOIN tbl_EQUITYMapping b
ON FundCode = SchemeCode_Client
LEFT JOIN tbl_EQUITYMapping c
ON Ticker = SecurityCode_Client
LEFT JOIN tbl_EQUITYMapping d
ON Broker = Brokercode_Client
The Access db engine requires parentheses in the FROM clause when your query includes more than one JOIN. I suspect this FROM clause version will be a step closer to something the db engine will accept.
FROM
((tbl_EQUITYINPUT a
LEFT JOIN tbl_EQUITYMapping b
ON FundCode = SchemeCode_Client)
LEFT JOIN tbl_EQUITYMapping c
ON Ticker = SecurityCode_Client)
LEFT JOIN tbl_EQUITYMapping d
ON Broker = Brokercode_Client
However, I'm uncertain whether the db engine will be confused sorting out which join field comes from which table source. I would prefix those field names with the proper table alias.
But I think your most direct route to joy for this may be to start with a new query in Design View in the query designer. Add tbl_EQUITYINPUT and 3 copies of tbl_EQUITYMapping and assign the aliases. Then set up your joins between them while still in Design View. The query designer understands the join rules which keep the engine happy, so will guide you to the correct join syntax. And it will also include the aliases with the field names in your joins.
I'm wondering if it's confused over which instance of tbl_EQUITYMapping to join against.
I'd expect to see your from block look a little more like this:
select *
FROM tbl_EQUITYINPUT a
LEFT JOIN tbl_EQUITYMapping b
ON a.FundCode = b.SchemeCode_Client
LEFT JOIN tbl_EQUITYMapping c
ON a.Ticker = c.SecurityCode_Client
LEFT JOIN tbl_EQUITYMapping d
ON a.Broker = d.Brokercode_Client
In this case, you are joining three different copies of tbl_EQUITYMapping with tbl_EQUITYINPUT. But I don't think that is what you want to do. If you are pulling data from tbl_EQUITYMapping, the select won't know which copy to pull it from.
I expect that SchemeCode_Client, SecurityCode_Client, and Brokercode_Client form a composite key for tbl_EQUITYMapping. In which case I would expect a from block that looked like this.
select *
FROM tbl_EQUITYINPUT a
LEFT JOIN tbl_EQUITYMapping b
ON (a.FundCode = b.SchemeCode_Client
AND a.Ticker = b.SecurityCode_Client
AND a.Broker = B.Brokercode_Client)
In this case, you'd get only one copy of tbl_EQUITYMapping
You could equally well do something like this:
select *
from tbl_EQUITYINPUT a,
tbl_EQUITYMapping b
where
(a.FundCode = b.SchemeCode_Client
AND a.Ticker = b.SecurityCode_Client
AND a.Broker = B.Brokercode_Client)
(the designer may or may not optimise that for you in the former version)

SQL: Get latest entries from history table

I have 3 tables
person (id, name)
area (id, number)
history (id, person_id, area_id, type, datetime)
In this tables I store the info which person had which area at a specific time. It is like a salesman travels in an area for a while and then he gets another area. He can also have multiple areas at a time.
history type = 'I' for CheckIn or 'O' for Checkout.
Example:
id person_id area_id type datetime
1 2 5 'O' '2011-12-01'
2 2 5 'I' '2011-12-31'
A person started traveling in area 5 at 2011-12-01 and gave it back on 2011-12-31.
Now I want to have a list of all the areas all persons have right now.
person1.name, area1.number, area2.number, area6.name
person2.name, area5.number, area9.number
....
The output could be like this too (it doesn't matter):
person1.name, area1.number
person1.name, area2.number
person1.name, area6.number
person2.name, area5.number
....
How can I do that?
This question is, indeed, quite tricky. You need a list of the entries in history where, for a given user and area, there is an 'O' record with no subsequent 'I' record. Working with just the history table, that translates to:
SELECT ho.person_id, ho.area_id, ho.type, MAX(ho.datetime)
FROM History AS ho
WHERE ho.type = 'O'
AND NOT EXISTS(SELECT *
FROM History AS hi
WHERE hi.person_id = ho.person_id
AND hi.area_id = ho.area_id
AND hi.type = 'I'
AND hi.datetime > ho.datetime
)
GROUP BY ho.person_id, ho.area_id, ho.type;
Then, since you're really only after the person's name and the area's number (though why the area number can't be the same as its ID I am not sure), you need to adapt slightly, joining with the extra two tables:
SELECT p.name, a.number
FROM History AS ho
JOIN Person AS p ON ho.person_id = p.id
JOIN Area AS a ON ho.area_id = a.id
WHERE ho.type = 'O'
AND NOT EXISTS(SELECT *
FROM History AS hi
WHERE hi.person_id = ho.person_id
AND hi.area_id = ho.area_id
AND hi.type = 'I'
AND hi.datetime > ho.datetime
);
The NOT EXISTS clause is a correlated sub-query; that tends to be inefficient. You might be able to recast it as a LEFT OUTER JOIN with appropriate join and filter conditions:
SELECT p.name, a.number
FROM History AS ho
JOIN Person AS p ON ho.person_id = p.id
JOIN Area AS a ON ho.area_id = a.id
LEFT OUTER JOIN History AS hi
ON hi.person_id = ho.person_id
AND hi.area_id = ho.area_id
AND hi.type = 'I'
AND hi.datetime > ho.datetime
WHERE ho.type = 'O'
AND hi.person_id IS NULL;
All SQL unverified.
You're looking for results where each row may have a different number of columns? I think you may want to look into GROUP_CONCAT()
SELECT p.`id`, GROUP_CONCAT(a.`number`, ',') AS `areas` FROM `person` a LEFT JOIN `history` h ON h.`person_id` = p.`id` LEFT JOIN `area` a ON a.`id` = h.`area_id`
I haven't tested this query, but I have used group concat in similar ways before. Naturally, you will want to tailor this to fit your needs. Of course, group concat will return a string so it will require post processing to use the data.
EDIT I thikn your question has been edited since I began responding. My query does not really fit your request anymore...
Try this:
select *
from person p
inner join history h on h.person_id = p.id
left outer join history h2 on h2.person_id = p.id and h2.area_id = h.area_id and h2.type = 'O'
inner join areas on a.id = h.area_id
where h2.person_id is null and h.type = 'I'