Chain of SQL subqueries within large query of JOINS - mysql

I am trying to structure a SQL query with complex nested select operation!
My original SQL query successfully JOINS around 30 tables, the data are retrieved as wanted! However every fetched record in the 30 tables
has many records in another table called (Comments)! What I want to do is to atribute every record in the (Comments table) to its record in the other
30 tables by IDs and retrieve them all together in one query. Yet this is not the only challenge, some of the 30 tables have in addition to the records in
(Comments table) more records in another table called (extras), so i am looking for additional subquery within the main subquery within
a LEFT JOIN inside the outter main query.
To make the idea more clear; without subquery the script will be as following:
$query = $mysqli->query("
SELECT
parent1.parent1_id,
parent1.child1_id,
parent1.child2_id,
parent1.child3_id,
parent2.parent2_id,
parent2.child1_id,
parent2.child2_id,
parent2.child3_id,
child1.child1_id,
child1.child1_content,
child2.child2_id,
child2.child2_content,
child3.child3_id,
child3.child3_content
FROM
parent1
LEFT JOIN child1
ON child1.child1_id = parent1.child1_id
LEFT JOIN child2
ON child2.child2_id = parent1.child2_id
LEFT JOIN child3
ON child3.child3_id = parent1.child3_id
LEFT JOIN followers
ON parent1.user_id = followers.followed_id
AND parent1.parent1_timestamp > followers.followed_timestamp
AND parent1.parent1_id NOT IN (SELECT removed.isub_rmv FROM removed)
AND parent1.parent1_hide = false
WHERE
followers.follower_id = {$_SESSION['info']}
{$portname_clause}
ORDER BY
parent1.parent1_timestamp DESC
LIMIT
{$postnumbers}
OFFSET
{$offset}
")
// Now fetching and looping through the retrieved data
while($row = $query->fetch_assoc()){
echo $row['child1_content'];
$subquery1 = $mysqli->query("SELECT extras.child1_id,
extras.extrasContent FROM extras WHERE extras.child1_id =
{$row['child1_id']}");
while($row1 = $subquery1->fetch_assoc()){
echo $row1['extrasContent'];
}
echo $row['child2_content'];
$subquery2 = $mysqli->query("SELECT extras.child2_id,
extras.extrasContent FROM extras WHERE extras.child2_id =
{$row['child2_id']}");
while($row2 = $subquery2->fetch_assoc()){
echo $row2['extrasContent'];
}
echo $row['child3_content'];
$subquery3 = $mysqli->query("SELECT extras.child3_id,
extras.extrasContent FROM extras WHERE extras.child3_id =
{$row['child3_id']}");
while($row3 = $subquery3->fetch_assoc()){
echo $row3['extrasContent'];
// Here i need to run additional query inside the subquery 3 to retrieve the (Comments table) data beside (extras table)
$subquery4 = $mysqli->query("SELECT comments.comment_id, comments.comment FROM comments WHERE comments.child3_id = {$row['child3_id']} OR comments.child3_id = {$row3['child3_id']}");
while($row4 = $subquery4->fetch_assoc()){
echo $row4['comment'];
}
}
} // No sane person would make such code
Because the code above would be totally rediclious i searched for a better way to carry it out, and thats where i came across the subquery
concept, but i do not know anything about subqueries, and shortly after i studied it i came up with this messy code, check it below!
I am not posting the origianl code here because it is too long, i am including a virtual example of the tables i want to apply the
query on in order to demonstrate the process.
SELECT
parent1.parent1_id,
parent1.child1_id,
parent1.child2_id,
parent1.child3_id,
parent2.parent2_id,
parent2.child1_id,
parent2.child2_id,
parent2.child3_id
FROM
parent1
LEFT JOIN
( SELECT
child1.child1_id,
child1.child1_content
FROM
child1
WHERE
child1.child1_id = parent1.child1_id ) child1
( SELECT extras.extrasID, extras.extrasContent
FROM
extras
WHERE
extras.child1_id = child1.child1_id )
ON parent1.child1_id = child1.child1_id
LEFT JOIN child2
( SELECT
child2.child2_id,
child2.child2_content
FROM
child2
WHERE
child2.child2_id = parent1.child2_id )
( SELECT
extras.extrasID,
extras.extrasContent
FROM
extras
WHERE
extras.child2_id = child2.child2_id )
ON parent1.child2_id = child2.child2_id
LEFT JOIN child3
( SELECT
child3.child3_id,
child3.child3_content
FROM
child3
WHERE
child3.child3_id = parent1.child3_id )
( SELECT
extras.extrasID,
extras.extrasContent
FROM
( SELECT
comments.comment_id,
comments.comment
FROM
comments
WHERE
comments.child3_id = extras.child3_id ) extras
JOIN child3
ON extras.child3_id = child3.child3_id )
ON parent1.child3_id = child3.child3_id
LEFT JOIN followers
ON parent1.user_id = followers.followed_id
AND parent1.parent1_timestamp > followers.follower_timestamp
AND parent1.parent1_id NOT IN (SELECT removed.isub_rmv FROM removed)
AND parent1.parent1_hide = false
WHERE
followers.follower_id = {$_SESSION['info']}
{$portname_clause}
ORDER BY
parent1.parent1_timestamp DESC
LIMIT
{$postnumbers}
OFFSET
{$offset} // <-- Sorry for the bad code formatting!
I am using MySql 5.6.37
I did not get the hang of the subquery concept yet, frankly i got lost and confused as i was studying it and for another reason too mentioned in the note below.
Note: I apologize in advance that i might not be on instant reply because where i live there is no electrecity or ADSL or phones and my
USB modem hardly gets signal, i have only two hours of averege three hours a day of electericity generated by a desil generator. i recharge
my laptop and check internet and the remaining one-two hours are for other life stuff.
I know the joke is on me as i am developing a web project without electricity or permanent internet. BUT LIFE DOES NOT GIVE EVERYTHING! lol.

This is how i solved the problem!
SELECT
parent1.parent1_id,
parent1.child1_id,
child1.child1_id,
child1.child1_content,
comments.comment_id,
comments.comment,
comments.child1_id
FROM parent1 LEFT JOIN
(
SELECT comments.comment_id, comments.comment, comments.child1_id
FROM
(
SELECT comments.comment_id,comments. comment, comments.child1_id
FROM comments
) comments JOIN child1
ON comments.child1_id = child1.child1_id
) comments
ON child1.child1_id = comments.
It needs some aliases and then it's good to go.

Related

How to pull in null values when searching for specific document name

I am having issues pulling in null values in my query. I am looking for patients who have a specific document name in their chart but also want to show patients who do not have this specific document name as well. Right now my code is only pulling in the patients with the document name History and Physical (Transcription) but I need to see Null values as well. Below is my code:
snip of code
SELECT CV3ClientVisit.ClientDisplayName, CV3ClientVisit.CurrentLocation, CV3ClientVisit.IDCode, CV3ClientVisit.VisitIDCode, CV3ClientVisit.VisitStatus, CV3ClientVisit.TypeCode, CV3ClientDocumentCUR.DocumentName
FROM CV3ClientVisit INNER JOIN
CV3ClientDocumentCUR ON CV3ClientVisit.GUID = CV3ClientDocumentCUR.ClientVisitGUID
WHERE (CV3ClientVisit.VisitStatus = 'ADM') AND (CV3ClientVisit.TypeCode = 'INPATIENT ADMIT') AND (CV3ClientDocumentCUR.DocumentName = 'History & Physical (transcription)' OR CV3ClientDocumentCUR.DocumentName IS NULL )
Use a LEFT JOIN with the condition in the ON clause:
SELECT cv.ClientDisplayName, cv.CurrentLocation, cv.IDCode,
cv.VisitIDCode, cv.VisitStatus, cv.TypeCode, cd.DocumentName
FROM CV3ClientVisit cv LEFT JOIN
CV3ClientDocumentCUR cd
ON cv.GUID = cd.ClientVisitGUID AND
cd.DocumentName = 'History & Physical (transcription)'
WHERE cv.VisitStatus = 'ADM' AND
cv.TypeCode = 'INPATIENT ADMIT' ;
I also added table aliases to simplify the query.

How can I get the latest package for all packages in SQL?

I have two tables, packages (with id, name as attributes) and releases (with url, upload_time, downloaded_bytes as attributes). Every package can have arbitrary many releases. I want a list of all packages with their latest release.
Currently, I have the following working code:
sql = ("SELECT `packages`.`id`, `name` FROM `packages`")
cursor.execute(sql)
packages = cursor.fetchall()
for pkg in packages:
sql = ("SELECT `url` FROM `releases` "
"WHERE `package_id` = %s "
"AND `downloaded_bytes` = 0 "
"ORDER BY `upload_time` DESC LIMIT 1")
cursor.execute(sql, (pkg['id'], ))
url = cursor.fetchone()
if url is not None:
package_url = url['url']
package_analysis.main(pkg['name'], package_url)
logging.info("Package '%s' done.", pkg['name'])
However, I think this is an ugly solution as I execute a lot of queries where I should only execute one query.
Can I do this in one query? How would the query look like?
Please note: I only want one result for each package. That means, the package numpy should only give the result for url="https://pypi.python.org/packages/cp35/n/numpy/numpy-1.10.1-cp35-cp35m-macosx_10_6_intel.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl" (version 1.10.1) and not 99 results.
What I've tried
SELECT
`packages`.`id`,
`packages`.`name`,
`releases`.`url`,
`releases`.`upload_time`,
`releases`.`release_number`
FROM
`packages`
JOIN
`releases` ON `releases`.`package_id` = `packages`.`id`
GROUP BY
`packages`.`name`
ORDER BY
`releases`.`upload_time` DESC
But that gives a seemingly random value for upload_time (and also url).
You can try this query:
select p.id, p.name, r.url, r.upload_time, r.release_number from
(select p.id, max(r.release_number) release_number from packages p
join releases r on p.id = r.package_id
group by p.id) a
join packages p on p.id = a.id
join releases r on r.release_number = a.release_number
It assumes that release_number is sortable, if not possible you can use max upload time instead.
Based on this answer (thank you Emiswelt) for mentioning it:
SELECT
`packages`.`id`,
`packages`.`name`,
`o`.`url`,
`o`.`upload_time`,
`o`.`release_number`
FROM
`releases` o
LEFT JOIN
`releases` b ON `o`.`package_id` = `b`.`package_id`
AND `o`.`upload_time` < `b`.`upload_time`
JOIN
`packages` ON `packages`.`id` = o.package_id
WHERE
`b`.`upload_time` is NULL
AND `o`.`downloaded_bytes` = 0
ORDER BY
`packages`.`name`
LIMIT 10
The query finishes execution within a fraction of a second.

how to perform count using nested select in a select statement

So here is the issue. I'm trying to write a new fillrate report because the one built in is not good enough... I'm trying to run a single select statement to return both, a count of how many times an item was ordered for a specific month, and then also a count of how many times it was invoiced/shipped in full.
This code is obviously wrong, I also currently have it restricted to only look at AUG of 2015, but that is just to simplify results during testing.
I can't figure out how to do the 2nd count... This is what I was trying (brain stuck on old for each loop logic):
select inv_mast.item_id,
inv_mast.item_desc,
"YEAR" = year(oe_line.required_date),
"MONTH" = month(oe_line.required_date),
"ORDERS" = count(1),
"HITS" = (
select count(1)
from invoice_line
where invoice_line.order_no = oe_line.order_no
and invoice_line.oe_line_number = oe_line.line_no
and invoice_line.qty_shipped = oe_line.qty_ordered
)
from oe_line,
inv_mast,
inv_loc
where inv_mast.inv_mast_uid = oe_line.inv_mast_uid
and inv_mast.delete_flag = 'N'
and inv_mast.inv_mast_uid = inv_loc.inv_mast_uid
and inv_loc.location_id = '101'
and year(oe_line.required_date) = '2015'
and month(oe_line.required_date) = '8'
group by inv_mast.item_id,
inv_mast.item_desc,
year(oe_line.required_date),
month(oe_line.required_date)
order by inv_mast.item_id
To me it would seem like you could rewrite the query to use a left join on the invoice_line table instead. Without any proper test data I can't guarantee it is correct, but I think it should be.
Besides the left join I also changed to explicit joins and moved the aliases as I don't think MySQL supports the alias = column syntax.
select inv_mast.item_id,
inv_mast.item_desc,
year(o.required_date) as "YEAR",
month(o.required_date) as "MONTH",
count(1) as "ORDERS",
count(invoice_line.order_no) as "HITS"
from oe_line o
join inv_mast on inv_mast.inv_mast_uid = o.inv_mast_uid
join inv_loc on inv_mast.inv_mast_uid = inv_loc.inv_mast_uid
left join invoice_line on invoice_line.order_no = o.order_no
and invoice_line.oe_line_number = o.line_no
and invoice_line.qty_shipped = o.qty_ordered
where inv_mast.delete_flag = 'N'
and inv_loc.location_id = '101'
and year(o.required_date) = '2015'
and month(o.required_date) = '8'
group by inv_mast.item_id,
inv_mast.item_desc,
year(o.required_date),
month(o.required_date)
order by inv_mast.item_id;

Simplify sql query to obtain one line per id

I have a multi-table SQL query.
My need is: The query should I generate a single line by 'etablissement_id' ... and all information that I want to be back in the same query.
The problem is that this query is currently on a table where "establishment" may have "multiple photos" and suddenly, my query I currently generates several lines for the same id...
I want the following statement - LEFT JOINetablissementContenuMultimediaON etablissement.etablissement_id = etablissementContenuMultimedia.etablissementContenuMultimedia_etablissementId - only a single multimedia content is displayed. Is it possible to do this in the query below?
Here is the generated query.
SELECT DISTINCT `etablissement`. * , `etablissementContenuMultimedia`. * , `misEnAvant`. * , `quartier`. *
FROM `etablissement`
LEFT JOIN `etablissementContenuMultimedia` ON etablissement.etablissement_id = etablissementContenuMultimedia.etablissementContenuMultimedia_etablissementId
LEFT JOIN `misEnAvant` ON misEnAvant.misEnAvant_etablissementId = etablissement.etablissement_id
LEFT JOIN `quartier` ON quartier_id = etablissement_quartierId
WHERE (
misEnAvant_typeMisEnAvantId =1
AND (
misEnAvant_dateDebut <= CURRENT_DATE
AND CURRENT_DATE <= misEnAvant_dateFin
)
)
AND (
etablissement_isActive =1
)
ORDER BY `etablissement`.`etablissement_id` ASC
LIMIT 0 , 30
Here is the code used ZF
public function find (){
$db = Zend_Db_Table::getDefaultAdapter();
$oSelect = $db->select();
$oSelect->distinct()
->from('etablissement')
->joinLeft('etablissementContenuMultimedia', 'etablissement.etablissement_id = etablissementContenuMultimedia.etablissementContenuMultimedia_etablissementId')
->joinLeft('misEnAvant', 'misEnAvant.misEnAvant_etablissementId = etablissement.etablissement_id')
->joinLeft('quartier', 'quartier_id = etablissement_quartierId ')
->where ('misEnAvant_typeMisEnAvantId = 1 AND (misEnAvant_dateDebut <= CURRENT_DATE AND CURRENT_DATE <= misEnAvant_dateFin) ')
->where ('etablissement_isActive = 1')
->order(new Zend_Db_Expr('RAND()'));
$zSql = $oSelect->__toString();
if(isset($_GET['debug']) AND $_GET['debug'] == 1)
echo $zSql ;
//die();
$oResultEtablissement = $db->fetchAll($oSelect);
return $oResultEtablissement ;
}
Can you help me?
Sincerely,
If you are looking to have only one of the media displayed out of many regardless of which it may be then you can just add a limit to the query? After that you can tweak the query for ASCending or DESCending perhaps?
Is this query supposed to have images (or image as it were) for one establishment, or one image each for each active establishment? I see you have a limit 0,30 which means you're likely paginating....
If the result you want is a search for only one establishment, and the first image it comes to would work fine .. just use "limit 1" and you'll only get one result.
I took the time to redo the whole model of the database ... and now it works. There was no solution for a system as flawed

configuring how to pull data out of 3 tables with mysql

Here is how the the database is layout. I can connect to DB fine.
I had it pullling from database two things but adding a third i can not get it to pull. I just need some help if i could ..
database - mmoore_drupal
table 1
table name = content_type_uprofile
data
vid= 19723
nid =19674
field_name_value = matthew moore
table 2
table name = location_instance
data
lid = 1521
vid = 19723
nid = 19674
table 3
table name = location
data
lid = 1521
street =
city =
country =
latitude =
longitude =
I am trying to pull name and then other info from the other two tables. But mainly i need to have name and other information from location. I thought i had to have the other table to associate the connection. Any help is appreciated.
$query = "SELECT content_type_uprofile.field_name_value,location.street,location.city
FROM location_instance,location,content_type_uprofile
WHERE location_instance.lid = location.lid and location_instance.nid=content_type_uprofile.nid"
$result = mysql_query($query) or die(mysql_error());
// Print out the contents of each row into a table
while($row = mysql_fetch_array($result)){
echo $row['nid']."-".$row['street']. " - ". $row['city'];
echo "<br />";
}
?>
Use this SQL (explicit join syntax):
SELECT
content_type_uprofile.nid,
location.street,
location.city
FROM
content_type_uprofile
INNER JOIN location_instance
ON (content_type_uprofile.nid = location_instance.nid)
INNER JOIN location
ON (location_instance.lid = location.lid)
The SQL that you posted is using implicit join SQL syntax.
I think for some reason, I think the line in your SQL:
WHERE location_instance.lid = location.lid and location_instance.nid=content_type_uprofile.nid
is filtering out all the rows from your result set. I'm not sure because I avoid the implicit syntax.
You were also missing the nid field which your PHP code is looking for in the result set.
As long as your data is correct (i.e. the fields that you are joining on have the right values), the SQL that I posted will work for you.
You ever done something with join's?
select *.locaition,
*.content_type_uprofile
from location_instance li
inner join location l on l.lid = li.lid
inner join content_type_uprofile ctu on ctu.vid = li.vid