I designed a db schema to hold patient appointments. Every hour is divided into equal 15 slots and the last two slots are always kept unused for emergency cases. When printing the appointment list, I need to have all the booked appointments along with empty row for 14 & 15th slot for each hour so receptionist can fill-in the unscheduled appointments by hand in the printed sheet.
appt_schedules:
id
slot_id
hour
scheduled_on
slots table - contains exactly 15 rows all the time.
id
start_time
end_time
The following query gives me the expected result except the empty row for 14 & 15th slot.
SELECT `appt_schedules`.* FROM `appt_schedules`
LEFT JOIN `slots` ON `appt_schedules`.`slot_id` = `slots`.`id`
WHERE `scheduled_on`='2015-04-17' LIMIT 10,10
I am also open to schema changes that can result in simple and efficient queries.
SELECT `appt_schedules`.*, `slots`.`slot_id` FROM `appt_schedules`
LEFT JOIN `slots` ON `appt_schedules`.`slot_id` = `slots`.`id`
WHERE `scheduled_on`='2015-04-17' and `slots`.`slot_id` between 1 and 15
Assuming of cours slots.slot_id holds the 15 equal alots as an int 1 to 15
Another way to write it a bit more optimized is:
SELECT `appt_schedules`.*, `slots`.`slot_id` FROM `appt_schedules`
LEFT JOIN `slots` ON `appt_schedules`.`slot_id` = `slots`.`id`
WHERE `scheduled_on`='2015-04-17' and `slots`.`slot_id` IS NOT NULL
Or even better:
SELECT `appt_schedules`.*, `slots`.`slot_id` FROM `appt_schedules`
JOIN `slots` ON `appt_schedules`.`slot_id` = `slots`.`id`
WHERE `scheduled_on`='2015-04-17'
****BEGIN REVISED EDIT****
If I understand correctly please try:
SELECT s.slot_id, aps.* FROM slots s
LEFT JOIN
(SELECT *, ROW_NUMBER()
over(PARTITION BY aps.slot_id
ORDER BY aps.slot_id
,aps.scheduled_on) AS RowId
FROM appt_schedules aps) aps
ON aps.slot_id = s.id
AND s.slot_id = aps.RowId
WHERE scheduled_on = '2015-04-17'
ORDER BY s.slot_id
Once again thanks for removing slos.slot_id....
select *
from slots s
left join appt_schedules as
on s.id = as.slot_id
where IFNULL(as.scheduled_on, '') in ('', '2015-04-17')
Related
It's a work in medical records. Goal is computing average value in days between two medical consultations, per patient, per care-unit, per year. I'm stuck with big records : for small units with less than 50 patients / 200 consultations, the below HQL query (for one care-unit/one year) is functional and relatively quick, but for greater medical activity, there is a "combinatory explosion" with a heavy load on database ... And my wish is to analyze 10 years for some 80 care-units... in one launch.
If you have any advice I would be very grateful!
SELECT
HB3 patient.pati_nip AS NIPP,
UPPER(cufm.cufm_libelle) AS CAT_UFM,
grp.unfo_libelle AS SECTEUR_DISP,
uf_ex.codeLibelle AS UNITE,
COUNT(DISTINCT raa.id) AS RAA,
COUNT(DISTINCT patient.id) AS PATIENTS,
ROUND(AVG(raa2.traa_date-raa.traa_date),1) AS DELAIMOY_J_INTER_RAA
FROM
Ide_patient AS patient
JOIN patient.pms_edgars AS redg
JOIN redg.bas_uf AS uf_ex
JOIN redg.pms_edgar_actes AS acte
JOIN acte.bas_catalogue_gen_by_Edgr_id_cage_nature AS type
JOIN acte.pms_raas as raa
JOIN patient.pms_edgars AS redg2
JOIN redg2.bas_uf AS uf_ex2
JOIN redg2.pms_edgar_actes AS acte2
JOIN acte2.bas_catalogue_gen_by_Edgr_id_cage_nature AS type2
JOIN acte2.pms_raas as raa2
JOIN uf_ex.bas_etablissement AS etab
JOIN uf_ex.bas_uf_by_Unfo_id_unfo_grp as grp
JOIN uf_ex.bas_categorie_ufm AS cufm
WHERE
etab.id = <ETAB>
AND raa.traa_date BETWEEN INVITE(D: Actes exportés effectués entre le ) AND INVITE(D: et le )
AND type.cage_code NOT LIKE 'R%'
AND uf_ex.id = INVITE(B:UF_MED_FILT_VAL: File active+nouveaux patients pour cette UF exécutante)
AND raa.traa_dat_export IS NOT NULL
AND raa2.traa_date = (SELECT MIN(raa3.traa_date)
FROM patient.pms_edgars AS redg3
JOIN redg3.bas_uf AS uf_ex3
JOIN redg3.pms_edgar_actes AS acte3
JOIN acte3.bas_catalogue_gen_by_Edgr_id_cage_nature AS type3
JOIN acte3.pms_raas as raa3
WHERE raa3.traa_dat_export IS NOT NULL
AND raa3.traa_date > raa.traa_date
AND uf_ex3.id = uf_ex
AND type3.cage_code NOT LIKE 'R%')
ORDER BY
patient.pati_nip, UPPER(cufm.cufm_libelle), grp.unfo_libelle, uf_ex.codeLibelle
https://stackoverflow.com/users/1766831/rick-james, here is the minimal query, with no delta computing, no "agregate" functions
SELECT
HB3 patient.id AS PATI_ID,
uf_ex.codeLibelle AS UNITE,
raa.traa_date AS DATE_CONSULT_DATE
FROM
Ide_patient AS patient
JOIN patient.pms_edgars AS redg
JOIN redg.bas_uf AS uf_ex
JOIN redg.pms_edgar_actes AS acte
JOIN acte.bas_catalogue_gen_by_Edgr_id_cage_nature AS type
JOIN acte.pms_raas as raa
JOIN uf_ex.bas_etablissement AS etab
WHERE
etab.id = <ETAB>
AND raa.traa_date BETWEEN INVITE(D: consultations between ) AND INVITE(D: and )
AND type.cage_code NOT LIKE 'R%'
AND uf_ex.id = INVITE(B:UF_MED_FILT_VAL: consultations done in this care-unit)
AND raa.traa_dat_export IS NOT NULL
ORDER BY
GROUP BY uf_ex.codeLibelle, patient.id, raa.traa_date
=> First letter of type.cage_code means "type of consultations" IN ('E','D','G','A','R'), and 'R' is excluded because patient is not present (meeting of the medical team)
=> goal is computing, for all consultations (except R) of a same patient, the delta betwen two contiguous consultations in a time interval. Date Format for raa.traa_date includes hours,minutes,seconds.
=> uf_ex.id is the ID of the medical care-unit for the actual consultation
Step 1. CREATE TABLE tbl with pati_id, unite, and consult_date. Also, have a 4th column that is AUTO_INCREMENT PRIMARY KEY; let's call it id. (If you are using 8.0 or 10.2, use a "CTE" and WITH.)
Step 2. Use the above 'minimal' query to populate the 3 columns, letting id populate itself. Be sure to include ORDER BY pati_id, consult_date. (and maybe unite?)
Step 3. ALTER TABLE tbl ADD INDEX(pati_id, id)
Step 4. Do a self-join of that table with itself, but offset the id:
SELECT pati_id,
DATEDIFF(t2.consult_date, t1.consult_date) AS gap
FROM tbl AS t1
JOIN tbl AS t2 ON t2.pati_id = t1.pati_id
AND t2.id = t1.id + 1
(I leave it to you to decide how UNITE fits in.)
I am building a query, limited by a specified # of seats, that selects a group of seats from a seat map that is divided into sections.
It attempts to grab all of the needed seats from the same section, but if that section doesn't have enough seats to satisfy the requested quantity, it grabs the remaining seats from the next section.
No good.
While I can probably figure out a convoluted way in PHP to take my result set, check for more than one section, and if so, skip the first few available seats in the section that doesn't have enough quantity available, and re-query again to ensure I get all of my needed seats in the next section.
Whew! Messy.
However, I'd like to figure this out in SQL. Code below.
SELECT et.id, et.event_id, et.seat_id, et.zone_id, s.seat_number, r.row_name, r.row_priority, sc.section_name, sc.section_priority, z.zone_name, s.seat_handicap, s.seat_obstructed, ticket_base_price, yth_boxoffice_ticket_fee, yth_online_ticket_fee, vendor_boxoffice_ticket_fee, vendor_online_ticket_fee
FROM yth_event_tickets et
LEFT JOIN yth_seats s ON et.seat_id = s.id
RIGHT JOIN yth_seatmap_rows r ON s.seat_row_id = r.id
RIGHT JOIN yth_seatmap_sections sc ON r.row_section_id = sc.id
RIGHT JOIN yth_seatmap_zones z ON sc.section_zone_id = z.id
WHERE event_id = 101252413
AND zone_id = 1
AND ticket_status = 1
AND s.seat_active = 1
ORDER BY r.row_priority ASC, sc.section_priority ASC, s.seat_priority ASC
LIMIT 4
I order by Row first, then Section. That way, it goes around the stadium and grabs the first Row from each Section first, and when those are filled, starts in Row 2, and so forth, so that it selects "best available" back.
As you can see, I only want 4 seats. I get 2 seats in Section B, but then Section B is full, so I get the remaining 2 seats from Section C. What I'd like to be able to do is skip over those last 2 seats in Section B and get my 4 seats in Section C. Furthermore, if Section C only has, say, < 4 remaining seats, and skip it as well and move on to Section D, etc...
First, count the number of available seats per section (or per zone, section, row - whatever you need), something like:
SELECT se.id, COUNT(*) AS seats_available
FROM yth_seats s
JOIN yth_rows r
ON s.row_id = r.id
JOIN yth_sections se
ON se.id = r.section_id
WHERE status = 1
GROUP BY se.id
With that, you can join that information back to your main query, limiting your results just to the section that has enough seats:
SELECT s.*
FROM (SELECT se.id, COUNT(*) AS seats_available
FROM yth_seats s
JOIN yth_rows r
ON s.row_id = r.id
JOIN yth_sections se
ON se.id = r.section_id
WHERE STATUS = 1
GROUP BY se.id) section_available
JOIN yth_sections se
ON se.id = section_available.id
JOIN yth_rows r
ON r.section_id = se.id
JOIN yth_seats s
ON s.row_id = r.id
WHERE s.status = 1
AND section_available.seats_available >= 4
-- your priorities
ORDER BY se.NAME
LIMIT 4
Working example on dbfiddle
You didn't mention which version of MySQL you're using, I'd normally go with a CTE instead of a subquery, but this feature might not yet be available to you.
Are you requiring all seats to be on the same row, or just in the same zone?
I'm supposing in the same row.
I'm not going to try to understand all your tables, so I'll imagine you have a view of seats:
zone,rowno,seatno
;WITH avail AS (SELECT TOP 1 zone,rowno FROM seats -- find the first row with 4 or more seats
GROUP BY zone,rowno
HAVING COUNT(*) >=4
ORDER BY zone,rowno)
-- select the first 4 seats from that row.
SELECT top 4 seats.* FROM avail INNER JOIN seats
ON avail.zone = seats.zone AND avail.rowno=seats.rowno
ORDER BY seatno
Here's the code:
SELECT tblitem.strItemCode, tblitem.strItemName, tblitemunit.strItemUnitName, tblvendor.strVendName, MAX(tblitemprice.dtmItemPasOf) AS Expr1,
tblitemprice.dblItemPAmount
FROM tblclassification INNER JOIN
tblitem ON tblclassification.strClasCode = tblitem.strItemClasCode INNER JOIN
tblitemprice ON tblitem.strItemCode = tblitemprice.strItemPItemCode INNER JOIN
tblitemunit ON tblitemprice.strItemPItemUnitCode = tblitemunit.strItemUnitCode INNER JOIN
tblvendor ON tblclassification.strClasCode = tblvendor.strVendClasCode AND tblitemprice.strItemPVendCode = tblvendor.strVendCode AND tblitem.deleted_at IS NULL
GROUP BY tblitem.strItemCode, tblitem.strItemName, tblitemunit.strItemUnitName, tblvendor.strVendName, tblitemprice.dblItemPAmount
And Here's the Result:
CODE NAME UNIT VENDOR DATE PRICE
ITEM101-Fudgee Bar-Piece-Imus Palengke 10/9/20165:03:32AM - 6.5
ITEM102-Yum Burger-Box-Jollibee Lumina Mall-10/9/2016 6:13:27 AM - 2500
ITEM102-Yum Burger-Piece-Jollibee Lumina Mall-10/9/2016 4:42:28 AM - 30
ITEM102-Yum Burger-Piece-Jollibee Lumina Mall-10/13/2016 12:37:31 PM- 35
ITEM102-Yum Burger Piece Jollibee Lumina Mall 10/14/2016 10:05:44 PM 40
What I want to happen is to fetch only the row with the latest price. Can someone help me please.
I want to fetch the Item101 and only the last row to ITEM102 since it is the latest.
If I understand correctly, you're looking for the last updated row price. It's easy to do so:
Order your data by their relevant timestamp in reversed order (ORDER BY <FIELD_NAME> DESC). From what I gather from your query and results, Expr1 is the latest date for a given price.
Only pick a single element (LIMIT 1). Since your data is already ordered in reverse chronological order, you're sure to pick the latest one.
The SQL for that would be
SELECT tblitem.strItemCode, tblitem.strItemName, tblitemunit.strItemUnitName, tblvendor.strVendName, MAX(tblitemprice.dtmItemPasOf) AS Expr1,
tblitemprice.dblItemPAmount
FROM tblclassification INNER JOIN
tblitem ON tblclassification.strClasCode = tblitem.strItemClasCode INNER JOIN
tblitemprice ON tblitem.strItemCode = tblitemprice.strItemPItemCode INNER JOIN
tblitemunit ON tblitemprice.strItemPItemUnitCode = tblitemunit.strItemUnitCode INNER JOIN
tblvendor ON tblclassification.strClasCode = tblvendor.strVendClasCode AND tblitemprice.strItemPVendCode = tblvendor.strVendCode AND tblitem.deleted_at IS NULL
GROUP BY tblitem.strItemCode, tblitem.strItemName, tblitemunit.strItemUnitName, tblvendor.strVendName, tblitemprice.dblItemPAmount
ORDER BY Expr1 DESC
LIMIT 1
Try it out !
I want to SELECT a field based on a ID value.
Products
PRODUCT_ID Name
19 Chair
20 Table
Product_fields
ID PRODUCT_ID TYPE DESCRIPTION
1 19 C White
2 19 S Modern
3 20 C Black
4 20 S Classic
I need a result like:
Product Type_C Type_S
Chair White Modern
Table Black Classic
I am able to produce this using two LEFT JOINs on the product_fields table but this slows down the query too much. Is there a better way?
Slows down the query how much? What is acceptable?
If you really don't want to use joins (you must have one join), then use views or nested queries. But I don't think they will be any faster, though you can give it a try.
See views at sqlfiddle
select p.PRODUCT_ID, p.Name, f.CDescription, f.SDescription
from Products p
join(
SELECT PRODUCT_ID, Max( CDescription ) as CDescription,
Max( SDescription ) as SDescription
FROM(
select PRODUCT_ID,
case Type when 'C' then Description end as CDescription,
case Type when 'S' then Description end as SDescription
from Fields
) x
group by PRODUCT_ID
) f
on f.PRODUCT_ID = p.PRODUCT_ID;
The complete statement is:
SELECT
NL.product_name,
PRD.product_sku AS product_sku,
CF.virtuemart_product_id AS virtuemart_product_id,
GROUP_CONCAT(distinct CFA.customsforall_value_name
ORDER BY CFA.customsforall_value_name ASC
separator ' | ' ) AS Name_exp_3,
ROUND((((prices.product_price * CALC.calc_value) / 100) + prices.product_price),
2) AS Prijs,
VMCF_L.custom_value AS latijn,
VMCF_T.custom_value AS THT
VMCF_B.custom_value AS Batch
from j25_virtuemart_products AS PRD
LEFT join j25_virtuemart_product_custom_plg_customsforall AS CF ON CF.virtuemart_product_id = PRD.virtuemart_product_id
join j25_virtuemart_product_prices AS prices ON PRD.virtuemart_product_id = prices.virtuemart_product_id
join j25_virtuemart_calcs AS CALC ON prices.product_tax_id = CALC.virtuemart_calc_id
join j25_virtuemart_products_nl_nl AS NL ON NL.virtuemart_product_id = PRD.virtuemart_product_id
LEFT join j25_virtuemart_product_customfields AS VMCF ON VMCF.virtuemart_product_id = PRD.virtuemart_product_id
LEFT join j25_virtuemart_custom_plg_customsforall_values AS CFA ON CFA.customsforall_value_id = CF.customsforall_value_id
LEFT JOIN j25_virtuemart_product_customfields AS VMCF_L ON VMCF.virtuemart_product_id = VMCF_L.virtuemart_product_id AND VMCF_L.virtuemart_custom_id = 16
LEFT JOIN j25_virtuemart_product_customfields AS VMCF_T ON VMCF.virtuemart_product_id = VMCF_T.virtuemart_product_id AND VMCF_T.virtuemart_custom_id = 3
LEFT JOIN j25_virtuemart_product_customfields AS VMCF_B ON VMCF.virtuemart_product_id = VMCF_B.virtuemart_product_id AND VMCF_B.virtuemart_custom_id = 18
WHERE
PRD.product_sku like '02.%'
group by PRD.virtuemart_product_id
order by NL.product_name;
Where the three SELECT results named 'Latijn', 'THT', and 'Batch' are the ones which I compared earlier as the black/white and classic/modern values.
Hope this makes any sense.
As you can see this involves a Virtuemart installation, so I cannot fiddle about to much with the schema.
When I exclude the bottom 3 JOINS and there related FIELDS, the query takes approx 0,5 seconds. With the JOINS and FIELDS included, the query takes almost 19 seconds.
I have created a view from this complete query which I query from my labeling application.
Thanks everyone! With your input I created:
select
NL.product_nameASproduct_name,
PRD.product_skuASproduct_sku,
CF.virtuemart_product_idASvirtuemart_product_id,
group_concat(distinctCFA.customsforall_value_name
order byCFA.customsforall_value_nameASC
separator ' | ') ASName_exp_3,
round((((prices.product_price*CALC.calc_value) / 100) +prices.product_price),
2) ASPrijs,
f.LatijnASLatijn,
f.THTASTHT,
f.BatchASBatch
from
(((((((j25_virtuemart_productsPRD
left joinj25_virtuemart_product_custom_plg_customsforallCFON ((CF.virtuemart_product_id=PRD.virtuemart_product_id)))
joinj25_virtuemart_product_pricespricesON ((PRD.virtuemart_product_id=prices.virtuemart_product_id)))
joinj25_virtuemart_calcsCALCON ((prices.product_tax_id=CALC.virtuemart_calc_id)))
joinj25_virtuemart_products_nl_nlNLON ((NL.virtuemart_product_id=PRD.virtuemart_product_id)))
left joinj25_virtuemart_product_customfieldsVMCFON ((VMCF.virtuemart_product_id=PRD.virtuemart_product_id)))
left joinj25_virtuemart_custom_plg_customsforall_valuesCFAON ((CFA.customsforall_value_id=CF.customsforall_value_id)))
left joinvw_batch_Latijn_THT_groupedfON ((f.virtuemart_product_id=PRD.virtuemart_product_id)))
where
(PRD.product_skulike '02.%')
group byPRD.virtuemart_product_id
order byNL.product_name``
Which takes 1.4 seconds to execute, a whole lot faster then the 19 seconds I started with.
I have three tables
Tasks with columns Taskid, Taskname
TaskAllocations with columns Taskid, EmpNum
TaskEntries with columns TaskId, EmpNum, WorkedDate, Hoursspent
Now I want to get all the task entries along a particular week. Here my problem is even if there is no Taskentry for a particular task I should get atleast a row with that TaskId, and Taskname with Hoursspent as Null in the query's resultset. I have been trying to get this with the below query.
SELECT A.TaskId,
B.TaskName,
SUM( C.HoursSpent ) as TotalHours ,
C.WorkedDate, C.Comments
FROM TaskAllocations A
LEFT OUTER JOIN TaskEntries C
ON A.TaskId = C.TaskId
AND A.EmpNum = C.EmpNum
INNER JOIN Tasks B
ON A.TaskId = B.TaskId
WHERE A.EmpNum =123456
AND C.WorkedDate
IN ('2010-01-17','2010-01-18','2010-01-19',
'2010-01-20','2010-01-21','2010-01-22','2010-01-23' )
GROUP BY A.TaskId, C.WorkedDate
ORDER BY A.TaskId,C.WorkedDate ASC ';
What I am getting for this SQL piece is if and only if there is an entry for a particular task id, then only i am getting a row for that. but what I want is to get atleast a row for each and every task that is available to a EmpNum. Even if I get one row for each TaskId and WorkedDate combination no issues. Please help me with this. Actual intention of this is to build a HTML two dimensional table with each task entry against date and task as shown below.
---------------------------------------------------------
TaskId TaskName Sun Mon Tue Wed Thu Fri Sat
---------------------------------------------------------
18 name1 2 3 4:30 3:30
19 name2
20 name3 4 2:30
22 name4 2:30
23 name5
24 name6 1:30 6
---------------------------------------------------------
So that this can be updated by the user for each year week. First I thought of group_concat but because of performance I am using normal group by query.
Note: for a particular taskid and workeddate there will be only one entry of hoursspent.
I have almost built the frontend. Please help me to get all task ids as above even if there is no entry. Do I need to use subquery.
don't user an inner join, use a left or right join, depending which values from which tables you want.
so:
SELECT *
FROM tasks t
LEFT JOIN taskentries te
ON t.id = te.id
which is the same statement as:
SELECT *
FROM tasksentries te
RIGHT JOIN tasks t
ON te.id = t.id
will get you all tasks, even if there is no taskentry
an inner join will only select rows when there are rows in both tables, left join selects all rows from the left (first) table and matching from the other row (if there is no such row, null will be the value of all columns). right join will do the oposite: select all rows from right (second) table and matching from left.
a LEFT JOIN b is the same as b RIGHT JOIN a
After rigorous testing of different options I came up with the below solution which will give the required results for me.
SELECT Final.TaskId,
Final.TaskName,
Tmp.HoursSpent AS TotalHours,
Tmp.WorkedDate
FROM (
SELECT A.TaskId, B.TaskName, A.EmpNum
FROM TaskAllocations A
INNER JOIN
Tasks B
ON ( A.TaskId = B.TaskId )
WHERE a.empnum = "333"
)Final
LEFT OUTER JOIN (
SELECT New.TaskId, New.EmpNum, New.WorkedDate, New.HoursSpent
FROM TaskEntries New
WHERE New.WorkedDate
IN
('2010-01-17','2010-01-18','2010-01-19',
'2010-01-20','2010-01-21','2010-01-22','2010-01-23' )
OR New.WorkedDate IS NULL
AND New.EmpNum = "333"
)Tmp
ON Tmp.TaskId = Final.TaskId
AND Tmp.EmpNum = Final.EmpNum
ORDER BY Final.TaskId, Tmp.WorkedDate ASC ;
The first query of mine in the question was not working as I was putting a condition on right table's column while doing Left Outer Join. Thanks to all for the support.