CakePHP custom query without own table - php

I have a query which joins other tables and insert a custom row named action with static value started. At a later stage I will have to UNION 2 more querys which contains another static value in the row custom.
How do I implement this without having an own table for this values? Im used to PDO where I can make an array with my entrys and loop through. All i see in the documentation is where you have a fix table with these records.
select a.StartTimestamp, b.Name, b.LastName, 'Started' AS 'action', c.translation
from a
inner join b on a.ID = b.ID
inner join c on b.ID = c.ID
group by a.StartTimestamp
order by a.StartTimestamp DESC
limit 5;

Related

Dynamically created sql not happy

I have a table which contains records of a 'widget' many of the columns contain just the Id of a record in a different table. When editing the widget record users are allowed to do a save even if it is incomplete. They can open it later and continue.
The problem I have, is when it is incomplete my query returns nothing because the where clause contain fields which have default 0 in them and there is no match in the other tables. Here is a sample of script which illustrates this problem.
select Client,Make,Model,Shape
from
widget,clients,makes,models,shapes
where
widget.ClientId = '3' and
widget.MakeId = makes.Id and
widget.ModelId = models.Id and
widget.ShapeId = shapes.Id
I am building this query dynamically using PHP so am trying to keep it as simple as possible. All sugestions welcome, thanks.
The problem is that you are using an implicit inner join (implicit meaning that you do the join in the where clause). Inner joins return matching records only, therefore if some of the data are incomplete, no records will be returned.
Use an outer join instead, that return all records from one of the tables in the join and the matching records from the other table (MySQL does not support full outer join, but this is not relevant here anyway).
Based on your description widget table is your main table, so use left join to join all other tables on widget to get the widget even if it is incomplete:
select c.Client, m.make, md.model, s.shape
from widget w
left join clients c on c.id = w.ClientId
left join makes m on m.id = w.MakeId
left join models md on md.id = w.ModelId
left join shapes s on s.id = w.ShapeId
select c.Client, m.make, md.model, s.shape
from widget w
join clients c on c.id = w.ClientId
join makes m on m.id = w.MakeId
join models md on md.id = w.ModelId
join shapes s on s.id = w.ShapeId
Use Joins instead of multiple tables in FROM Clause.
Instead of direct join use LEFT JOIN so that no matter if there is records from other tables, still first table entries will be returned:
SELECT Client, Make, Model, Shape
FROM widget
LEFT JOIN clients ON widget.ClientId = widget.Id
LEFT JOIN makes ON widget.MakeId = makes.Id
LEFT JOIN models ON widget.ModelId = models.Id
LEFT JOIN shapes ON widget.ShapeId = shapes.Id
WHERE widget.ClientId = 3
Table columns should be in lower case
Table names should be in singular form, e.g. model, shape
Foreign keys should be in other table. Instead of widget having ModelId, model should have widget id
I may be wrong if relations are different

mysql merge query in one output with different fields

I have four db table,
tableA, tableB, tableC, tableD
I am listing records with join. These all working fine.
My query is like following.
Select DISTINCT A.*, B.loginname, B.email, C.full_name, C.address1, C.state, C.city, D.*
FROM (
tableA AS A
INNER JOIN tableB AS B ON B.user_id = A.id
INNER JOIN tableC AS C ON C.user_id = A.id
INNER JOIN tableD AS D ON D.user_id = A.id
) WHERE A.id = '269' ORDER BY A.created_date DESC
Now, issue is i have created four another table with almost same details but there are different fields and columns. So i can not use UNION.
tableE, tableF, tableG, tableH
i want to merge output to display. And output should be ORDER BY A.created_date OR ORDER BY E.created_date
Advanced thanks...
You can use UNION ALL (or UNION, if you want duplicates removed) between two tables with differently named columns by declaring name aliases.
SELECT 'first' AS source, a,b,c,d
FROM first
UNION ALL
SELECT 'second' AS source, q AS a, r AS b, s AS c, t AS d
FROM second
This will yield a result set (virtual table) with columns named source,a,b,c,d.

PDO JOIN LEFT - fetch result - no result [duplicate]

I'm doing some maintenance work on a database application and I've discovered that, joy of joys, even though values from one table are being used in the style of foreign keys, there's no foreign key constraints on the tables.
I'm trying to add FK constraints on these columns, but I'm finding that, because there's already a whole load of bad data in the tables from previous errors which have been naively corrected, I need to find the rows which don't match up to the other table and then delete them.
I've found some examples of this kind of query on the web, but they all seem to provide examples rather than explanations, and I don't understand why they work.
Can someone explain to me how to construct a query which returns all the rows with no matches in another table, and what it's doing, so that I can make these queries myself, rather than coming running to SO for every table in this mess that has no FK constraints?
Here's a simple query:
SELECT t1.ID
FROM Table1 t1
LEFT JOIN Table2 t2 ON t1.ID = t2.ID
WHERE t2.ID IS NULL
The key points are:
LEFT JOIN is used; this will return ALL rows from Table1, regardless of whether or not there is a matching row in Table2.
The WHERE t2.ID IS NULL clause; this will restrict the results returned to only those rows where the ID returned from Table2 is null - in other words there is NO record in Table2 for that particular ID from Table1. Table2.ID will be returned as NULL for all records from Table1 where the ID is not matched in Table2.
I would use EXISTS expression since it is more powerful, you can e.g. more precisely choose rows you would like to join. In the case of LEFT JOIN, you have to take everything that's in the joined table. Its efficiency is probably the same as in the case of LEFT JOIN with null constraint.
SELECT t1.ID
FROM Table1 t1
WHERE NOT EXISTS (SELECT t2.ID FROM Table2 t2 WHERE t1.ID = t2.ID)
SELECT id FROM table1 WHERE foreign_key_id_column NOT IN (SELECT id FROM table2)
Table 1 has a column that you want to add the foreign key constraint to, but the values in the foreign_key_id_column don't all match up with an id in table 2.
The initial select lists the ids from table1. These will be the rows we want to delete.
The NOT IN clause in the where statement limits the query to only rows where the value in the foreign_key_id_column is not in the list of table 2 ids.
The SELECT statement in parenthesis will get a list of all the ids that are in table 2.
Let we have the following 2 tables(salary and employee)
Now i want those records from employee table which are not in salary.
We can do this in 3 ways:
Using inner Join
select * from employee
where id not in(select e.id from employee e inner join salary s on e.id=s.id)
Using Left outer join
select * from employee e
left outer join salary s on e.id=s.id where s.id is null
Using Full Join
select * from employee e
full outer join salary s on e.id=s.id where e.id not in(select id from salary)
Where T2 is the table to which you're adding the constraint:
SELECT *
FROM T2
WHERE constrained_field NOT
IN (
SELECT DISTINCT t.constrained_field
FROM T2
INNER JOIN T1 t
USING ( constrained_field )
)
And delete the results.
From similar question here MySQL Inner Join Query To Get Records Not Present in Other Table I got this to work
SELECT * FROM bigtable
LEFT JOIN smalltable ON bigtable.id = smalltable.id
WHERE smalltable.id IS NULL
smalltable is where you have missing records, bigtable is where you have all the records. The query list all the records that not exist in smalltable but exists on the bigtable. You could replace id by any other matching criteria.
I Dont Knew Which one Is Optimized (compared to #AdaTheDev
) but This one seems to be quicker when I use (atleast for me)
SELECT id FROM table_1 EXCEPT SELECT DISTINCT (table1_id) table1_id FROM table_2
If You want to get any other specific attribute you can use:
SELECT COUNT(*) FROM table_1 where id in (SELECT id FROM table_1 EXCEPT SELECT DISTINCT (table1_id) table1_id FROM table_2);
You could opt for Views as shown below:
CREATE VIEW AuthorizedUserProjectView AS select t1.username as username, t1.email as useremail, p.id as projectid,
(select m.role from userproject m where m.projectid = p.id and m.userid = t1.id) as role
FROM authorizeduser as t1, project as p
and then work on the view for selecting or updating:
select * from AuthorizedUserProjectView where projectid = 49
which yields the result as shown in the picture below i.e. for non-matching column null has been filled in.
[Result of select on the view][1]
You can do something like this
SELECT IFNULL(`price`.`fPrice`,100) as fPrice,product.ProductId,ProductName
FROM `products` left join `price` ON
price.ProductId=product.ProductId AND (GeoFancingId=1 OR GeoFancingId
IS NULL) WHERE Status="Active" AND Delete="No"
SELECT * FROM First_table
MINUS
SELECT * FROM another
How to select rows with no matching entry in Both table?
select * from [dbo].[EmppDetails] e
right join [Employee].[Gender] d on e.Gid=d.Gid
where e.Gid is Null
union
select * from [dbo].[EmppDetails] e
left join [Employee].[Gender] d on e.Gid=d.Gid
where d.Gid is Null

Mysql query inner join on most recent date (today, yesterday, or before)

I'm attempting to pull the latest pricing data from a table on an Inner Join. Prices get updated throughout the day but aren't necessary updated at midnight.
The following query works great when the data is updated on prices by the end of the day. But how do I get it to get yesterdays data if today's data is blank?
I'm indexing off of a column that is formatted like this date_itemnumber => 2015-05-22_12341234
SELECT h.*, collection.*, history.price
FROM collection
INNER JOIN h ON collection.itemid=h.id
INNER JOIN history ON collection.itemid=history.itemid
AND concat('2015-05-23_',collection.itemid)=history.date_itemid
WHERE h.description LIKE '%Awesome%'
Production Query time: .046 sec
To be clear, I want it to check for the most up to date record for that item. Regardless on if it is today, yesterday or before that.
SQLFiddle1
The following query gives me the desired results but with my production dataset it takes over 3 minutes to return results. As my dataset gets larger, it would take longer. So this can't be the most efficient way to do this.
SELECT h.*, collection.*, history.price
FROM collection
INNER JOIN h ON collection.itemid=h.id
INNER JOIN history ON collection.itemid=history.itemid
AND (select history.date_itemid from history WHERE itemid=collection.itemid GROUP BY date_itemid DESC LIMIT 1)=history.date_itemid
WHERE h.description LIKE '%Awesome%'
Production Query time: 181.140 sec
SQLFiddle2
SELECT x.*
FROM history x
JOIN
( SELECT itemid
, MAX(date_itemid) max_date_itemid
FROM history
-- optional JOINS and WHERE here --
GROUP
BY itemid
) y
ON y.itemid = x.itemid
AND y.max_date_itemid = x.date_itemid;
http://sqlfiddle.com/#!9/975f5/13
This should works:
SELECT h.*, collection.*, history.price
FROM collection
INNER JOIN h ON collection.itemid=h.id
INNER JOIN(
SELECT a.*
FROM history a
INNER JOIN
( SELECT itemid,MAX(date_itemid) max_date_itemid
FROM history
GROUP BY itemid
) b ON b.itemid = a.itemid AND b.max_date_itemid = a.date_itemid
) AS history ON history.itemid = collection.itemid
WHERE h.description LIKE '%Awesome%'
I don't know if this take a lot of execution time. Please do try it, since you might have more data in your tables it will be a good test to see the query execution time.
This is actually a fairly common problem in SQL, at least I feel like I run into it a lot. What you want to do is join a one to many table, but only join to the latest or oldest record in that table.
The trick to this is to do a self LEFT join on the table with many records, specifying the foreign key and also that the id should be greater or less than the other records' ids (or dates or whatever you're using). Then in the WHERE conditions, you just add a condition that the left joined table has a NULL id - it wasn't able to be joined with a more recent record because it was the latest.
In your case the SQL should look something like this:
SELECT h.*, collection.*, history.price
FROM collection
INNER JOIN h ON collection.itemid=h.id
INNER JOIN history ON collection.itemid=history.itemid
-- left join history table again
LEFT JOIN history AS history2 ON history.itemid = history2.itemid AND history2.id > history.id
-- filter left join results to the most recent record
WHERE history2.id IS NULL
AND h.description LIKE '%Awesome%'
This is another approach that cuts one inner join statement
select h.*,his.date_itemid, his.price from history his
INNER JOIN h ON his.itemid=h.id
WHERE his.itemid IN (select itemid from collection) AND h.description LIKE '%Awesome%' and his.id IN (select max(id) from history group by history.itemid)
you can try it here http://sqlfiddle.com/#!9/837a8/1
I am not sure if this is what you want but i give it a try
EDIT: modified
CREATE VIEW LatestDatesforIds
AS
SELECT
MAX(`history`.`date_itemid`) AS `lastPriceDate`,
MAX(`history`.`id`) AS `matchingId`
FROM `history`
GROUP BY `history`.`itemid`;
CREATE VIEW MatchDatesToPrices
AS
SELECT
`ldi`.`lastPriceDate` AS `lastPriceDate`,
`ldi`.`matchingId` AS `matchingId`,
`h`.`id` AS `id`,
`h`.`itemid` AS `itemid`,
`h`.`price` AS `price`,
`h`.`date_itemid` AS `date_itemid`
FROM (`LatestDatesforIds` `ldi`
JOIN `history` `h`
ON ((`ldi`.`matchingId` = `h`.`id`)));
SELECT c.itemid,price,lastpriceDate,description
FROM collection c
INNER JOIN MatchDatesToPrices mp
ON c.itemid = mp.itemid
INNER JOIN h ON c.itemid = h.id
Difficult to test the speed on such a small dataset but avoiding 'Group By' might speed things up. You could try conditionally joining the history table to itself instead of Grouping?
e.g.
SELECT h.*, c.*, h1.price
FROM h
INNER JOIN history h1 ON h1.itemid = h.id
LEFT OUTER JOIN history h2 ON h2.itemid = h.id
AND h1.date_itemid < h2.date_itemid
INNER JOIN collection c ON c.itemid = h.id
WHERE h2.id IS NULL
AND h.description LIKE '%Awesome%'
Changing this line
AND h1.date_itemid < h2.date_itemid
to actually work on a sequential indexed field (preferably unique) will speed things up too. e.g. order by id ASC

MYSQL Join with Null value returned

I have a query thats joining two table, using the GROUP_CONCAT to get a comma separated list which is then being mapped to an array in an object
SQL:
$sql = "SELECT *,
GROUP_CONCAT(climb_attributes.attribute_id) as climb_attributes
FROM climbs
LEFT JOIN climb_attributes ON
(climbs.id = climb_attributes.climb_id)
GROUP BY climb_id
ORDER BY climbs.id";
PHP
$all_climb_profiles[$climb->id]->attributes = explode(",", $climb->climb_attributes);
Nearly working perfectly, except I currently only get back results IF the climb_attributes table contains the climb id. Essentially a climb can still exist even if it doesn't have any attributes, but at the moment it has to have an attribute to be returned in the results.
I also need to join it to another table to get the attribute name for the attribute id...if you can help with that as well that would be great, I'm hoping I can figure that out though.
First, you should not be using * to select from all tables when using group by. You can safely take all the columns from the climb table.
The problem is that you are aggregating on a column in the second table, rather than the first. And, it is NULL if there is no match. So, a better query is:
SELECT c.*, GROUP_CONCAT(ca.attribute_id) as climb_attributes
FROM climbs c LEFT JOIN
climb_attributes ca
ON c.id = ca.climb_id
GROUP BY c.id
ORDER BY c.id;
EDIT:
If you want to list the strings, then something like this should work:
SELECT c.*, GROUP_CONCAT(a.name) as climb_attributes
FROM climbs c LEFT JOIN
climb_attributes ca
ON c.id = ca.climb_id LEFT JOIN
attributes a
ON ca.attribute_id = c.id
GROUP BY c.id
ORDER BY c.id

Categories