MySQL CONCAT values on SELF JOIN - mysql

l'm working on a query for an ajax auto suggestion feature on my website and need a little help.
I'm using the following query:
SELECT
CONCAT(b.cat_name, ' > ', a.cat_name) as value,
a.cat_id as data
FROM categories a
LEFT JOIN categories b
ON b.cat_id = a.cat_parent_id
WHERE a.cat_path LIKE '%health%' OR a.cat_slug LIKE '%health%' OR a.cat_name LIKE '%health%' OR a.cat_legacy_path LIKE '%health%'
ORDER BY a.cat_path ASC LIMIT 500
The problem is coming up with root categories where parent_cat_id IS NULL
I'd still like them to appear in my results however as it stands now, the results are coming back empty for the root categories.
Can someone help me tweak this query so that l can accomplish what l'm looking to do?
If it makes any difference l'm doing this with CodeIgniter 3, so here's the actual active record code l'm using on the query:
$search_term = strtolower($search_term);
$this->db->select("CONCAT(b.cat_name, ' > ', a.cat_name) as value, a.cat_id as data");
$this->db->from('categories a');
$this->db->like('a.cat_path', $search_term, 'both', false);
$this->db->or_like('a.cat_slug', $search_term, 'both', false);
$this->db->or_like('a.cat_name', $search_term, 'both', false);
$this->db->or_like('a.cat_legacy_path', $search_term, 'both', false);
$this->db->join('categories b', 'b.cat_id = a.cat_parent_id', 'left');
$this->db->order_by("a.cat_path", 'ASC');
$this->db->limit('500');
$query = $this->db->get();
Ideally l'd like the root categories (where cat_parent_id IS NULL) to just be selected as a.cat_name as value as apposed to CONCAT(b.cat_name, ' > ', a.cat_name) as value.
Any help would be greatly appreciated.

You just need to make it conditional:
SELECT
COALECSE(CONCAT(b.cat_name, ' > ', a.cat_name), a.cat_name) as value,
or, checking more precisely for this exact cause of NULL,
SELECT
IF(a.cat_parent_id IS NULL, a.cat_name, CONCAT(b.cat_name, ' > ', a.cat_name)) as value,
So in your case in codeigniter that would translate to something like:
$this->db->select("COALECSE(CONCAT(b.cat_name, ' > ', a.cat_name), a.cat_name) as value, a.cat_id as data");

Related

Codeigniter's Model with QueryBuilder JOIN query

Really new to working with CI4's Model and struggling to adapt my existing MySQL JOIN queries to work with the examples in its User Guide.
I have adapted part of my code like so:
public function brand_name($brand_name_slug)
{
return $this->asArray()
->where('availability', 'in stock')
->where('sku !=', '')
->where('brand_name_slug', $brand_name_slug)
->groupBy('gtin')
->orderBy('brand_name, subbrand_name, product, size, unit')
->findAll();
}
It works fine. I have looked at examples, and figured out I can add the code ->table('shop a') and it still works, but I also need to to add the following JOIN statement:
JOIN (SELECT gtin, MIN(sale_price) AS sale_price FROM shop GROUP BY gtin) AS b ON a.gtin = b.gtin AND a.sale_price = b.sale_price
As soon as I add ->join('shop b', 'a.gtin = b.gtin and a.sale_price = b.sale_price') I get a '404 - File Not Found' error.
When I look at all examples of CI4 joins and adapt my code to fit, my foreach($shop as $row) loop generates a 'Whoops...' error because they end with a getResult() or getResultArray - instead of findAll().
Which is the way forward, and do I need to change my foreach loop.
Full MySQL statement:
SELECT * FROM shop a JOIN (SELECT gtin, MIN(sale_price) AS sale_price FROM shop GROUP BY gtin) AS b ON a.gtin = b.gtin AND a.sale_price = b.sale_price WHERE availability = 'in stock' AND sku != '' AND brand_name_slug = $brand_name_slug GROUP BY gtin ORDER BY brand_name, subbrand_name, product, size
Query builders have their limits. That's why the query method exists. If you have a complex query I'd advise you to just use $this->query();.
It will make you lose less time and effort converting something you know already works. And in the top of that, while converting complex queries you usually end up using the query builder but with big part of your SQL in it.
In your model extending CodeIgniter\Model :
$query = $this->db->query("SELECT * FROM shop a JOIN (SELECT gtin, MIN(sale_price) AS sale_price FROM shop GROUP BY gtin) AS b ON a.gtin = b.gtin AND a.sale_price = b.sale_price WHERE availability = 'in stock' AND sku != '' AND brand_name_slug = \$brand_name_slug GROUP BY gtin ORDER BY brand_name, subbrand_name, product, size");
// your array result
$result_array = $query->getResultArray();
// your object result
$result_object = $query->getResult();
BaseBuilder Class in Codeigniter expects the first join parameter to be the table name. So try passing the table name and join it on the table name itself. I haven't personally used the table aliases so I might also be wrong.
Following are the parameter that the JOIN query expects :
public function join(string $table, string $cond, string $type = '', bool $escape = null)
Here, it expects the first name be a table, so try out by switching aliases for the table's name directly.
For your second part of query, It would be better if you could show the whole error rather than just posting the first of the error.
Managed to figure it out in the end:
public function brand_name($brand_name_slug)
{
return $this
->db
->table('shop a')
->select()
->join('(SELECT sku, MIN(sale_price) AS sale_price FROM shop GROUP BY sku) AS b', 'a.sku = b.sku AND a.sale_price = b.sale_price')
->where('availability', 'in stock')
->where('a.sku !=', '')
->where('brand_name_slug', $brand_name_slug)
->groupBy('a.sku')
->orderBy('brand_name, subbrand_name, product, size, unit')
->get()
->getResult();
}
Thanks for all your pointers!

Where clause query

So I have query the output is supposed to show all medically trained staff and all volunteers that have first aid:
SELECT Concat (s.staff_fname, ' ', s.staff_lname) AS Staff_Name,
s.medically_trained,
s.staff_email_address,
Concat(v.volunteer_fname, ' ', v.volunteer_lname) AS Volunteer_Name,
v.first_aid,
v.volunteer_email_address
FROM volunteer v
JOIN staff s
ON v.volunteerid = s.staffid
WHERE s.medically_trained = ' yes'
AND v.first_aid = ' yes'
however when I execute my code it says empty set. What am I doing wrong?
I am a beginner coder so please dont judge if ive made an obvious mistake lol
THANK YOU!
First remove the space from 'yes' , it may also cause this issue.You may get the not null value in result.
If it is still blank then try to run the where clause individually on both of the tables,
Select *
FROM volunteer v where v.first_aid = 'yes';
and
select * from staff s
WHERE s.medically_trained = 'yes' ;
and check whether any one of it is giving you null as a result, if yes then your answer is correct.
Remove the spaces in where section:
SELECT Concat (s.staff_fname, ' ', s.staff_lname) AS Staff_Name,
s.medically_trained,
s.staff_email_address,
Concat(v.volunteer_fname, ' ', v.volunteer_lname) AS Volunteer_Name,
v.first_aid,
v.volunteer_email_address
FROM volunteer v
JOIN staff s
ON v.volunteerid = s.staffid
WHERE s.medically_trained = 'yes' /* <-- */
AND v.first_aid = 'yes' /* <-- */
Try this:
SELECT CONCAT(s.staff_fname,' ', s.staff_lname)AS Name,s.medically_trained AS Trained,s.staff_email_address AS Email_Address,'STAFF' cType
FROM staff s
WHERE s.medically_trained = ' yes'
UNION ALL
SELECT Concat(v.volunteer_fname, ' ', v.volunteer_lname) AS Name,v.first_aid AS Trained,v.volunteer_email_address AS Email_Address,'VOLUNTEER' cType
FROM volunteer v
WHERE v.first_aid = ' yes'
I used UNION ALL for the two table because I dont think Volunteer_ID will be Equivalent to STAFF_ID. I included cType Column for you to identify if the record was from Staff table or Volunteer
If you do a CONCAT('foo', 'null'), the result will be null. Check to see if that's not the case.
Also, it could be from either the JOIN clause or WHERE clause. Try running the query without JOIN and then without the WHERE clause and see if you'll still have empty sets.

SQL query: NULL values that should not be NULL when using aggregate function with left join

i dont have much expiriance with SQL and i am trying to crack my head on this query.
i have 3 tables: Projects, Calculator and partialBilling
(note: the 'calculator' columns you see at the code ive added 'k','l','m' etc are real...i didnt gave them those names...).
the query is working fine but part of the values that i am expecting from the aggregate function ('sumofTotal' column) are returning as null values and and they should not be null.
I would be grateful if someone point out the mistake in the query.
SELECT Projects.SpCall,Projects.CustName,Projects.CustNumber
,Projects.ReceiveDate,Projects.StartDate,Projects.ProjectType
,Calculator.AN,Projects.Professional,Projects.PmUserName
,Projects.AcountManager,Projects.CrmCallNum,Projects.ProjectCategory
,Projects.CallNum,Projects.ContactName,Projects.ContactPhone
,Projects.ContactEmail,Projects.HiddenNote,Projects.RowColor
, Projects.HeaderCellText,
SUM(Calculator.K + Calculator.L + Calculator.M + Calculator.N + Calculator.AD + Calculator.AR) AS sumofTotal
,partialBilling.Ammount FROM Projects LEFT JOIN Calculator ON Projects.SpCall=Calculator.AQ
LEFT JOIN partialBilling ON Projects.SpCall = partialBilling.spCall
WHERE PmUserName= 'JOHN DOE'AND OpertionalStatus
<> 'Billed' AND OpertionalStatus<> 'Finished' AND
OpertionalStatus<> 'Passed To Billing' AND OpertionalStatus<> 'Scanning'
AND OpertionalStatus<> 'Ended'
AND OpertionalStatus<> 'Green Billing'
AND (GeneralStatus= 'Passed To Project Manager'
OR GeneralStatus= 'Astrategic Project')
GROUP BY Projects.SpCall,Projects.CustName,Projects.CustNumber
,Projects.ReceiveDate,Projects.StartDate,Projects.ProjectType
,Calculator.AN,Projects.Professional,Projects.PmUserName
,Projects.AcountManager,Projects.CrmCallNum,Projects.ProjectCategory
,Projects.CallNum,Projects.ContactName,Projects.ContactPhone
,Projects.ContactEmail,Projects.HiddenNote,Projects.RowColor
, Projects.HeaderCellText,partialBilling.Ammount;
Instead of proprietary IFNULL better use Standard SQL COALESCE:
SUM(COALESCE(Calculator.K,0) + COALESCE(Calculator.L,0), ...`
Or maybe a bit more efficient:
SUM(COALESCE(Calculator.K,0)) + SUM(COALESCE(Calculator.L,0)), ...`
Try to use IFNULL()
SUM(IFNULL(Calculator.K,0) + ... + IFNULL(Calculator.AR,0)) AS sumofTotal
You can use an expression like ifnull('column_name' , '') in place of column_name.

mysql query with multiple not criteria

Hi I have a query which is working but I would like to remove certain values from the results.
At the moment I have WHERE (pafaddresses.CountryName !="England") but I would like this to be Where is not England and where is not Wales, I cannot get it to work for two != statements, I have tried Where !="England" or "Wales", without any success.
Any help would be appreciated
SELECT
visits.VisitPk,
visits.ClientFk,
visits.ClientSiteFk,
visits.AssessorFk,
visits.VisitStartDate,
visits.VisitEndDate,
visits.Duration,
visits.VisitStatus,
visits.TargetDate,
CONCAT(MONTHNAME(TargetDate), ' ', YEAR(TargetDate)) AS TargetMonth,
pafaddresses.PostCode,
visits.`Long`,
visits.Lat,
pafaddresses.id,
pafaddresses.CountryName,
CONCAT(Clients.ClientName, ', ', clientsites.SiteName, ', ', MONTHNAME(TargetDate)) AS Description
FROM visits
INNER JOIN clientsites ON visits.ClientSiteFk = clientsites.ClientSitePk
LEFT OUTER JOIN pafaddresses ON clientsites.ActualPAF = pafaddresses.id
INNER JOIN Clients ON visits.ClientFk = Clients.ClientPk
WHERE (pafaddresses.CountryName !="England")
ORDER BY visits.TargetDate, visits.VisitStartDate
You can do that either with
WHERE (pafaddresses.CountryName != 'England' AND pafaddresses.CountryName != 'Wales')
or
WHERE pafaddresses.CountryName NOT IN ('England', 'Wales')

how to translate a very long mysql query with select and join to zend framework 1.11 model

I have this mysql query:
SELECT
freeAnswers.*,
(SELECT `districtCode`
FROM `geodatas`
WHERE `zipCode` = clients.zipCode
GROUP BY `zipCode`
LIMIT 0, 1) as districtCode,
clients.zipCode,
clients.gender,
clients.startAge,
clients.endAge,
clients.mail,
clients.facebook,
surveys.customerId,
surveys.activityId,
surveys.name as surveyName,
customers.companyName,
activities.name as activityName
FROM freeAnswers,
clients,
surveys,
customers,
activities
WHERE freeAnswers.surveyId = surveys.id
AND surveys.customerId = customers.id
AND activities.id = surveys.activityId
AND clients.id = freeAnswers.clientId
AND customers.id = 1
ORDER BY activityName asc
LIMIT 0, 10
the query is correct on my mysql server but when I try to use it in Zend Framework 1.11 model
I get this error: Mysqli prepare error: Operand should contain 1 column(s)
Please, could anyone help me to make it run well?
Best Regards,
Elaidon
Here is some code that should work. Zend_Db_Select doesn't really provide a way to select from multiple tables in the FROM clause without using a JOIN so this feels a bit hackish to me in regards to one small part of the query. Your best bet will probably be to rewrite the query using JOINs where appropriate.
$subselect = $db->select()
->from('geodatas', 'districtCode')
->where('zipCode = clients.zipCode')
->group('zipCode')
->limit(1, 0);
$from = $db->quoteIdentifier('freeAnswers') . ', ' .
$db->quoteIdentifier('clients') . ', ' .
$db->quoteIdentifier('surveys') . ', ' .
$db->quoteIdentifier('customers') . ', ' .
$db->quoteIdentifier('activities');
$select = $db->select()
->from(array('activities' => new Zend_Db_Expr($from)),
array('freeanswers.*',
'districtCode' =>
new Zend_Db_Expr('(' . $subselect . ')'),
'clients.zipCode', 'clients.gender', 'clients.startAge',
'clients.endAge', 'clients.mail', 'clients.facebook',
'clients.customerId', 'clients.activityId',
'surveyName' => 'surveys.name', 'customers.companyName',
'activityName' => 'activities.name'))
->where('freeAnswers.surveyId = surveys.id')
->where('surveys.customerId = customers.id')
->where('activities.id = surveys.activityId')
->where('clients.id = freeAnswers.clientId')
->where('customers.id = ?', 1)
->order('activityName ASC')
->limit(10, 0);
The only reason I say it is hackish is because of the line:
->from(array('activities' => new Zend_Db_Expr($from)),
Since from() really only works with one table, I create a Zend_Db_Expr and specify the correlation as the last table name in the expression. If you don't pass a Zend_Db_Expr, it will either quote your comma separated table name incorrectly, or if you pass an array of table names, it just uses the first. When you pass a Zend_Db_Expr with no name, it defaults to use AS t which also doesn't work in your case. That is why I put it as is.
That returns the exact SQL you provided except for the last thing mentioned. Here is actually what it returns:
SELECT
`freeanswers`.*,
(SELECT `geodatas`.`districtCode`
FROM `geodatas`
WHERE (zipCode = clients.zipCode)
GROUP BY `zipCode`
LIMIT 1) AS `districtCode`,
`clients`.`zipCode`,
`clients`.`gender`,
`clients`.`startAge`,
`clients`.`endAge`,
`clients`.`mail`,
`clients`.`facebook`,
`clients`.`customerId`,
`clients`.`activityId`,
`surveys`.`name` AS `surveyName`,
`customers`.`companyName`,
`activities`.`name` AS `activityName`
FROM `freeAnswers`,
`clients`,
`surveys`,
`customers`,
`activities` AS `activities`
WHERE (freeAnswers.surveyId = surveys.id)
AND (surveys.customerId = customers.id)
AND (activities.id = surveys.activityId)
AND (clients.id = freeAnswers.clientId)
AND (customers.id = 1)
ORDER BY `activityName` ASC
LIMIT 10
So that will work but eventually you will want to rewrite it using JOIN instead of specifying most of the WHERE clauses.
When dealing with subqueries and Zend_Db_Select, I find it easy to write each subquery as their own queries before writing the final query, and just insert the subqueries where they need to go and Zend_Db handles the rest.
Hope that helps.