Doctrine Query Adding Random Order By to End of Query From Subquery - sql

I've been trying for hours now but everytime i create a DQL query with a subquery, it seems to add an ORDER BY to the end that should be apart of the subquery even though i haven't explicitly asked for an ORDER BY.
Here's my DQL:
Doctrine_Query::create()
->select('r.*')
->from($table->getTableName() . ' r')
->innerJoin('r.Product p')
->where('r.is_published = 1')
->andWhere('NOT EXISTS (
SELECT pm.product_id
FROM ProductMedia pm
WHERE pm.product_id = p.id
)');
and heres the resulting query:
SELECT r.id AS r__id, r.created_sf_guard_user_id AS r__created_sf_guard_user_id, r.author_id AS r__author_id, r.published_at AS r__published_at, r.teaser_web AS r__teaser_web, r.teaser_newsletter AS r__teaser_newsletter, r.verdict AS r__verdict, r.product_id AS r__product_id, r.award_recommended AS r__award_recommended, r.award_editors_choice AS r__award_editors_choice, r.with_photos_tab AS r__with_photos_tab, r.first_look AS r__first_look, r.review_type AS r__review_type, r.overall_score AS r__overall_score, r.comments_id AS r__comments_id, r.review_price AS r__review_price, r.reviewed_at AS r__reviewed_at, r.adtech_keywords AS r__adtech_keywords, r.last_synced AS r__last_synced, r.standout_url AS r__standout_url, r.show_on_homepage AS r__show_on_homepage, r.best_deals_copy AS r__best_deals_copy, r.preview_to_review_at AS r__preview_to_review_at, r.created_at AS r__created_at, r.updated_at AS r__updated_at, r.is_published AS r__is_published, r.publish_start AS r__publish_start, r.publish_end AS r__publish_end FROM review r INNER JOIN product p ON r.product_id = p.id WHERE (r.is_published = 1 AND (NOT (EXISTS (SELECT p2.product_id AS p2__product_id FROM product_media p2 WHERE (p2.product_id = p.id))))) ORDER BY p2.position ASC
This is very strange behaviour and not sure whats causing it.
Any help is appreciated and if I'm missing anything to help please ask and i'll edit the question.
Thanks.

Related

Rewrite query in FOR LOOP to single query

Could you please help me to rewrite the following query inside of FOR LOOP query to single query without loop:
for rec in select distinct sp.student_id, v.test_id, v.name, p.version_id from student_pages sp, pages p, versions v where p.id = sp.page_id and v.id = p.version_id order by student_id, test_id, name LOOP
select STRING_AGG(cast(p.index as varchar), ';' ORDER BY p.index) as lost_page_indices into l_lost_page_indices from pages p left join student_pages sp on p.id = sp.page_id and sp.student_id = rec.student_id where p.version_id = rec.version_id and sp.page_id is null;
end loop;
In the final query I need the following fields: sp.student_id, v.test_id, v.name and lost_page_indices.
First cut: Take the query you're looping over, turn it into a subquery, and use it as a join table.
select STRING_AGG(cast(p.index as varchar), ';' ORDER BY p.index)
as lost_page_indices into l_lost_page_indices
from pages p
join (
select distinct sp.student_id, v.test_id, v.name, p.version_id
from student_pages sp
join pages p on p.id = sp.page_id
join versions v on v.id = p.version_id
) as rec on rec.student_id = sp.student_id
left join student_pages sp on p.id = sp.page_id and
sp.student_id = rec.student_id
where p.version_id = rec.version_id and
sp.page_id is null
order by rec.student_id, rec.test_id, rec.name
I reorgnaized the subquery using join syntax for easier reading.
order by cannot be relied on to survive a join, so it moves into the outer query.
There's no group by so I'm not sure if an aggregate function will work.
And, as others have pointed out, string_agg() is not a built in Oracle function. You have to make it yourself.
That can be simplified, there's a lot of redundancy between the two joins. That subquery is putting together student_pages, pages, and versions which can all be done with a normal join. The only thing left is the distinct sp.student_id which can be better done with a group by sp.student_id.
select STRING_AGG(cast(p.index as varchar), ';' ORDER BY p.index)
as lost_page_indices into l_lost_page_indices
from pages p
left join student_pages sp on sp.page_id = p.id
join versions v on v.id = p.version_id
where sp.page_id is null
group by sp.student_id
order by sp.student_id, v.test_id, v.name
I'm not 100% sure that's an equivalent query, but it should get you started. This makes it a lot clearer what the query is doing: find orphaned pages and stick them into a table.

How can I make this SQL query more efficient?

I have a query trying to pull data from multiple tables but when I run it, it takes a really long time (So long I haven't even been able to wait long enough). I know it's extremely inefficient and wanted to get some input as to how it can be written better. Here it is:
SELECT
P.patient_name,
LOH.patient_id,
LOH.requesting_location,
LOH.sample_date,
LOH.lab_doing_work,
L.location_name,
LOD.test_code,
LOD.test_rdx,
LSR.tube_type
FROM
mis_db.dbo.lab_order_header AS LOH,
mis_db.dbo.patient AS P,
mis_db.dbo.lab_order_detail AS LOD,
mis_db.dbo.lab_sample_rule AS LSR,
mis_db.dbo.location AS L
WHERE
LOH.requesting_location = '000839' AND
LOH.lab_order_id = LOD.lab_order_id AND
LOH.sample_date IN ('05/28/2015', '05/29/2015')
--LOH.patient_id = LOD.patient_id
--LOD.sample_date = LOH.sample_date
ORDER BY
P.patient_name DESC
try this (or something like it)
SELECT P.patient_name,
lo.patient_id, lo.requesting_location,
lo.sample_date, lo.lab_doing_work,
l.location_name, d.test_code, d.test_rdx,
d.tube_type
FROM mis_db.dbo.lab_order_header lo
join mis_db.dbo.patient p on p.patient_id = lo.Patient_id
join mis_db.dbo.lab_order_detail d on d.lab_order_id = lo.lab_order_id
join mis_db.dbo.lab_sample_rule r on r.rule_id = lo.ruleId -- ????
join mis_db.dbo.location l on l.locationid = lo.requesting_location
WHERE lo.requesting_location = '000839' AND
lo.sample_date IN ('05/28/2015', '05/29/2015')
ORDER BY p.patient_name DESC
I ended up going with the following and was able to get the results I wanted:
SELECT LOH.patient_id,
patient_name,
[mis_db_rpt].[common].[string_date_format](LOD.sample_date) AS
[Draw Date],
test_description,
LOD.test_code,
LOH.lab_doing_work,
tube_type,
L.short_name
FROM [mis_db].[dbo].[lab_order_header]
LOH
INNER JOIN
[mis_db].[dbo].[lab_order_detail]
LOD
ON LOH.lab_order_id = LOD.lab_order_id
INNER JOIN
[mis_db].[dbo].[patient]
P
ON P.patient_id = LOD.patient_id
INNER JOIN
[mis_db].[dbo].[sample_tube]
ST
ON LOD.sample_id = ST.sample_id
INNER JOIN
[mis_db].[dbo].[location] AS
L
ON LOH.lab_doing_work = L.location_id
INNER JOIN
[mis_db].[dbo].[lab_test] AS
LT
ON LOD.test_code = LT.test_code
WHERE LOH.requesting_location = '000839' AND
LOD.sample_date IN ('05/28/2015', '05/29/2015')
ORDER BY LOD.sample_date,
patient_name,
LOD.patient_id,
test_description
I would try
Click to run the estimated execution plan in SSMS and see if it suggests any missing indexes. I would think a non clustered index on lo.requesting_location and sample_date might help with the filter
Also in desc index on p.patient_name may help with the performance of the order by.
Try changing the IN date filter to "between '05/28/2015' and '05/29/2015'

SQL - include this record if these other records are included

Over simplifying here, but I need help. Let's say I have a SQL statement like this:
SELECT * FROM Policy p
JOIN OtherPolicyFile o on o.PolicyId = p.PolicyId
WHERE OtherPolicyFile.Status IN (9,10)
OK, so here is the story. I need to also pull any OtherPolicyFile where the Status = 11, but ONLY if there is a matching OtherPolicyFile with a status 9 or 10 as well.
In other words, I would not normally pull an OtherPolicyFile with status 11, but if that policy also has an OtherPolicyFile with a status 9 or 10, then I need to also pull any OtherPolicyFiles with a status of 11.
There is probably a really easy way to write this, but I'm frazzled at the moment and it is not coming to me without jumping through hoops. Any help would be appreciated.
Perform one extra left join and test the left joined table for NULL:
SELECT p.*, o.*
FROM Policy p
JOIN OtherPolicyFile o on o.PolicyId = p.PolicyId
LEFT JOIN OtherPolicyFile o9or10
on o9or10.PolicyId = p.PolicyId and o9or10.Status IN (9,10)
WHERE o.Status IN (9,10)
OR o.Status = 11 AND o9or10.PolicyId is NOT NULL
GROUP BY <whatever key you need>
But beware - you need to use GROUP BY so that the added LEFT JOIN doesn't duplicate lines. I cannot propose proper key because I don't know your schema, so fill in appropriate one (possibly the primary ID of OtherPolicyFile? So something like o.ID in your case? But I really don't know)
I would add a subquery to see if 9 or 10 exists. Here's the fiddle: http://sqlfiddle.com/#!3/1a68c/2
SELECT * FROM Policy p
JOIN OtherPolicyFile o on o.PolicyId = p.PolicyId
WHERE o.Status IN (9,10)
OR (o.Status = 11 AND
exists (select * from OtherPolicyFile innerO
where innerO.PolicyId = p.PolicyId
and (innerO.Status = 9 or innerO.Status=10)))
I believe this will give the best possible performance. Note no joins.
it will only execute on sql server 2005+
Be aware that you can't 'select *' and have identical column names in both tables for this to work. So you have to specify which columns you need.
SELECT * from
(
SELECT <your columns>,
statuscheck = min(nullif(o.status, 11)) over (partition by o.PolicyId)
FROM Policy p
JOIN OtherPolicyFile o on o.PolicyId = p.PolicyId
WHERE o.Status IN (9,10,11)
) a
WHERE statuscheck is not null

What could create a syntax error if you take a SQL query and perform an UNION with itself?

I have this strange error in SQL Server 2005 where I take a working query, add the UNION keyword below it and then copy the query again. In my opinion, this should always be working, but it is not. I get the message 'Incorrect syntax near the keyword 'union'.
What could create this problem ?
To be more specific, here is the complete query :
select distinct deliveries.id, orders.id, 20 + sum(orders.mass1) as allowed_duration
from features_resources
inner join features on features.id = featureid
inner join orders on orders.id = features_resources.resourceid
inner join orderinformations on orders.id = orderinformations.orderid
inner join deliveries on orderinformations.deliveryid = deliveries.id
where features.name = 'O_FRAIS'
and (deliveries.ID IN
(SELECT ID
FROM dbo.DeliveriesInExportedSchedule))
group by deliveries.id, features.name ,orders.id order by deliveries.id
union
select distinct deliveries.id, orders.id, 20 + sum(orders.mass1) as allowed_duration
from features_resources
inner join features on features.id = featureid
inner join orders on orders.id = features_resources.resourceid
inner join orderinformations on orders.id = orderinformations.orderid
inner join deliveries on orderinformations.deliveryid = deliveries.id
where features.name = 'O_FRAIS'
and (deliveries.ID IN
(SELECT ID
FROM dbo.DeliveriesInExportedSchedule))
group by deliveries.id, features.name ,orders.id order by deliveries.id
I have tried to reproduce the error on a smaller query, by starting from a simple query and adding features one by one (inner join, nested queryes, group by, sum,....) but failed to reproduce the error again.
Any idea ?
It is actually the order by deliveries.id in the top half that causes the problem.
The order by needs to apply to the whole query.
Example Syntax
SELECT v1.number
FROM master.dbo.spt_values v1
WHERE v1.number > 2000
UNION
SELECT v2.number
FROM master.dbo.spt_values v2
WHERE v2.number < 10
ORDER BY v1.number
Try putting the individual SELECTs in parentheses:
(SELECT ... )
UNION
(SELECT ... )
The way you have it now, the second WHERE and GROUP BY clauses are ambiguous - should that apply to the SELECT, or to the UNION? I don't have any way to tell, and neither has your DB server.

MySQL LIMIT on a LEFT JOIN

My query:
SELECT issues.*,
comments.author AS commentauthor,
comments.when_posted AS commentposted
FROM issues
LEFT JOIN (SELECT *
FROM comments
ORDER BY when_posted DESC
LIMIT 1) AS comments ON issues.id=comments.issue
ORDER BY IFNULL(commentposted, issues.when_opened) DESC
My problem with it is the "LIMIT 1" on the third line. That limits all comments to only the newest one, so only issues with the newest comment will be reported back as having a comment at all.
If I removed the "LIMIT 1" part from there, I'd get a row for every comment in an issue, and that's not what I want. What I want is only the newest comment for each issue.
In any case, I'm not sure if my IFNULL part even works because that's not where I'm up to in debugging yet.
So how would I achieve what I wanted?
Try:
SELECT i.*,
c.author AS commentauthor,
c.when_posted AS commentposted
FROM ISSUES i
LEFT JOIN COMMENTS c ON c.issue = i.id
JOIN (SELECT c.issue,
MAX(c.when_posted) 'max_when_posted'
FROM COMMENTS c
GROUP BY c.issue) mwp ON mwp.issue = c.issue
AND mwp.max_when_posted = c.when_posted
ORDER BY COALESCE(c.when_posted, i.when_opened) DESC
SELECT issues.*,
comments.author AS commentauthor,
comments.when_posted AS commentposted
FROM issues
LEFT JOIN ( SELECT c1.issue, c1.author, c1.when_posted
FROM comments c1
JOIN
(SELECT c2.issue, max(c2.when_posted) AS max_when_posted
FROM comments c2
GROUP BY issue) c3
on c1.issue = c3.issue and c1.when_posted = c3.max_when_posted
) AS comments ON issues.id=comments.issue
ORDER BY COALESCE(commentposted, issues.when_opened) DESC
Edit
Since MySql does not have CTE's after all, try this:
SELECT i.*
c.author AS CommentAuthor,
c.when_posted AS CommentPosted
FROM Issues i
LEFT JOIN
(SELECT issue, MAX(when_posted) AS LastPostDate
FROM comments GROUP BY issue) ic ON ic.issue = i.id
LEFT JOIN Comment c ON c.issue = i.id AND c.when_posted = ic.LastPostDate
ORDER BY COALESCE(ic.LastPostDate, issues.when_opened) DESC