I have a question regarding a two table join. In this case Table1 (booking table) and Table2 (Booking Entries).
I need a query to get all the rows from Table1 WHERE the member_id in Table2 (exists only here not in table1) and Vip_id in BOTH tables can be searched.
SELECT vb.* , DATE_FORMAT(vb.bookingdate, '%W %D %M') bookingdate, DATE_FORMAT(vb.bookingrsvp, '%W %D %M') bookingrsvp, concat(sl.state, ' - ', sl.store) store, sl.ADD1, sl.ADD2, sl.SUBURB, sl.PHONE , ve.vip_entry_leadid
FROM vip_booking vb
INNER JOIN storelocator sl ON (vb.storeid = sl.id )
LEFT JOIN vip_entries ve ON (vb.vipid = ve.vip_id AND ve.vip_entry_leadid = '" . $_GET["leadid"] . "')
WHERE vb.vipid = " . $_GET["vipid"] . "
AND DATE(vb.bookingdate) >= CURDATE()
AND ve.vip_entry_leadid IS NULL
AND ve.vip_id IS NULL
GROUP BY vb.storeid ORDER BY sl.state, sl.store
Basically what I am trying to achieve here is select ALL bookings from ALL Stores part of a particular VIP EVENT that the CURRENT LOGGED IN USER hasn't already had an entry too? If it was a single field ie. vip_entries.vip_id = vip_booking.vipid THEN that would be okm however a user can be in the entries table multiple times provided that it is a DIFFERENT event?
The above query works however I don't know if I have written it correctly as I would like to use joins and avoid sub-queries.
Can you post some sample data in the question please.
Your query appears to do a LEFT JOIN on vip_entries, and then checks for NULL in 2 fields to ensure no record is found (might be better to check the unique id field of vip_entries, if one exists). But you then bring back the value vip_entries.vip_entry_leadid which will always be null.
Further you are using GROUP BY store_id. This will bring back one row per store_id, but the other values will be from an undefined row for that store_id (in most databases this would fail). I suspect looking at your description you actually want to bring back one row per event / store id (which is probably a unique combination) in which case it would seem you do not need the GROUP BY.
The above query works however I don't know if I have written it correctly
Unfortunately, while preceding SQL works in MySQL, your query is not valid in ANSI SQL. Only GROUP BY column and aggregation function can be SELECTed
I would like to use joins and avoid sub-queries.
As far as I know without sub-queries, you can only fetch vb.storeid as follows
SELECT vb.storeid
FROM vip_booking vb
INNER JOIN storelocator sl ON (vb.storeid = sl.id )
LEFT JOIN vip_entries ve ON (vb.vipid = ve.vip_id AND ve.vip_entry_leadid = '$leadid')
WHERE vb.vipid = $vipid
AND DATE(vb.bookingdate) >= CURDATE()
AND ve.vip_entry_leadid IS NULL
AND ve.vip_id IS NULL
GROUP BY vb.storeid;
Proper SQL with sub-queries:
In my opinion Following query is formal SQL for what you want.
SELECT
vb.*,
DATE_FORMAT(vb.bookingdate, '%W %D %M') bookingdate,
DATE_FORMAT(vb.bookingrsvp, '%W %D %M') bookingrsvp,
CONCAT(sl.state, ' - ', sl.store) store,
sl.ADD1, sl.ADD2, sl.SUBURB, sl.PHONE , ve.vip_entry_leadid
FROM
(SELECT DISTINCT
vb.storeid
FROM
vip_booking vb
LEFT JOIN
vip_entries ve ON (vb.vipid = ve.vip_id AND ve.vip_entry_leadid = '" . $_GET["leadid"] . "'
WHERE
vb.vipid = " . $_GET["vipid"] . "
AND DATE(vb.bookingdate) >= CURDATE()
AND ve.vip_entry_leadid IS NULL
AND ve.vip_id IS NULL) x
INNER JOIN
storelocator sl ON (x.storeid = sl.id)
ORDER BY
sl.state, sl.store;
Related
I'm stuck with this problem for days and still can't think of a solution.Maybe i'm just making it too complex.
So my query looks like this.
SELECT distinct Cast(Table1.Date as Date) AS 'Date', Concat(Round(SUM((ISNULL(Price.Morning,0) + ISNULL(Price.Day,0) + ISNULL(Price.Evening,0))*Tickets.Count),2),' €') AS 'Total'
FROM Price,Tickets,Table1
WHERE Tickets.Price_ID = Price.Price_ID AND
Tickets.Table1_ID = Table1.Table1_ID
GROUP BY Date
;
The query should display distinct dates and total SUM should be displayed that was earned in the specific date.
As you can see there still are matching dates
EDIT. I don't know if i understood the criticism correctly but i applied these changes.
SELECT distinct Cast(Tabl1.Date as Date) AS 'Date', Concat(Round(SUM((ISNULL(Price.Morning,0) + ISNULL(Price.Day,0) + ISNULL(Price.Evening,0))*Tickets.Count),2),' €') AS 'Total'
FROM Table1
JOIN Tickets ON Tickets.Table1_ID = Table1.Table1_ID
JOIN Price ON Tickets.Price_ID = Price.Price_ID
GROUP BY Date
;
And i also understand that i need to provide more information for solution.
All the ..Concat(Round(SUM((ISNULL(Price.Morning,0) + ISNULL(Price.Day,0) + ISNULL(Price.Evening,0))*Tickets.Count),2),' €') AS 'Total' .. Is necessary because in the database there are Null values, thatš why there i use ISNULL function and 0 in case it is NULL because otherwise it brakes all the calculations.You can see that table here: I edited the column names so you could see(table itself is in different language)
(The column without name is not required for this)
Use explicit join not coma separated old join method and use subquery for format of total . No need distinct
select 'Date', concat(total,' €') from
(
SELECT Cast(Table1.Date as Date) AS 'Date',
SUM(Price.Morning+Price.Day+Price.Evening) as total
FROM Price join
Tickets on Tickets.Price_ID = Price.Price_ID
join Table1 on Tickets.Table1_ID = Table1.Table1_ID
GROUP BY Date
) as t
I have a valid MySQL Query that selects the latest occupancy percentage of a table from each community entered in my DB, but it seems to be scanning the entire DB of entries as the lookup time takes roughly 3-4 seconds.
With the details provided in the query below, can someone provide me with a faster/better way to lookup the latest timestamp field for each community? - I need the query to select every community entered, with the latest timestamp, but the limit for each community selected should be 1 (meaning community named "Test Community" will have possibly hundreds of submissions but I need the latest entered Timestamp selected, along with the same selection for every community entered in the table)
SELECT t1.reportID, t1.communityID, t1.region, t1.percentOccupied,
t1.TIMESTAMP, Communities.fullName
FROM NightlyReports t1
INNER JOIN Communities On t1.communityID = Communities.communityID
WHERE t1.TIMESTAMP = ( SELECT MAX( TIMESTAMP ) FROM NightlyReports WHERE
t1.communityID = NightlyReports.communityID )
AND t1.region = 'GA' ORDER BY percentOccupied DESC
In my experience, correlated subqueries often have rather poor performance; try this instead:
SELECT t1.reportID, t1.communityID, t1.region, t1.percentOccupied
, t1.TIMESTAMP, Communities.fullName
FROM NightlyReports AS t1
INNER JOIN Communities ON t1.communityID = Communities.communityID
INNER JOIN (
SELECT communityID, MAX( TIMESTAMP ) AS lastTimestamp
FROM NightlyReports
WHERE region = 'GA'
GROUP BY communityID
) AS lastReports ON t1.communityID = lastReports.communityID
AND t1.TIMESTAMP = lastReports.lastTimestamp
WHERE t1.region = 'GA'
ORDER BY percentOccupied DESC
Your query is fine. For this query (which is rewritten just a bit):
SELECT nr.reportID, nr.communityID, nr.region, nr.percentOccupied,
nr.TIMESTAMP, c.fullName
FROM NightlyReports nr INNER JOIN
Communities c
ON nr.communityID = c.communityID
WHERE nr.TIMESTAMP = (SELECT MAX(nr2.TIMESTAMP)
FROM NightlyReports nr2
WHERE nr.communityID = nr2.communityID
) AND
nr.region = 'GA'
ORDER BY percentOccupied DESC;
You want indexes on:
NightlyReports(region, timestamp, communityid)
NightlyReports(communityid, timestamp)
Communities(communityID) (this may already exist)
The correlated subquery is not per se a problem.
I'm running into some trouble with SQL:
Basically I'm trying to get a result set back that contains a sum of ALL questions asked to employees (grouped by company) and also add the "onetime_items" which are manually added items in a different table.
I currently have this SQL statement (I'm using MySQL):
SELECT
CONCAT_WS(
', ', count(DISTINCT CONCAT(emailaddress, '_', a.id)),
(
SELECT GROUP_CONCAT(items SEPARATOR '; ') as OneTimeItems
FROM (
SELECT CONCAT_WS(
': ', oi.item_name, SUM(oi.item_amount)
) items
FROM onetime_item oi
WHERE oi.company_id = e.company_id
AND oi.date BETWEEN '2015-12-01'
AND LAST_DAY('2015-12-01')
GROUP BY oi.item_name
) resulta
)
) as AllItems,
e.id,
LEFT(e.firstname, 1) as voorletter,
e.lastname
FROM question q
LEFT JOIN employee e ON q.employee_id = e.id
WHERE 1=1
AND YEAR(created_at) = '2015'
AND MONTH(created_at) = '12'
GROUP BY e.company_id
Now I get the following error:
Fatal error: Uncaught exception 'PDOException' with message 'SQLSTATE[42S22]: Column not found: 1054 Unknown column 'e.company_id' in 'where clause'
The dates used are dummy dates.
This column DOES exist in the table employee and the left join works ( I tried entering an id manually instead of using the column reference and it worked, I got the right result back)
Any idea as to why the reference to e.company_id fails?
Thanks to dba.stackexchange.com
link: https://dba.stackexchange.com/questions/126339/subquery-cant-find-column-from-superquerys-join
Answer by ypersillycubeᵀᴹ
Here's the answer that was posted there, hopefully someone else will profit as well from this!
The cause of the problem was identified by #Phil in the comments: in the comments:
Probably because it's nested too deep
You have 2 layers of nesting and the reference of table e cannot "see" through these 2 layers in MySQL.
Correlated inline subquery can usually be converted to derived tables and then LEFT joined to the other tables in the FROM clause but they have to be turned into uncorrelated (in MySQL. In other DBMS, you could use a LATERAL join or the similar OUTER APPLY.
A first rewrite to get the job done:
SELECT
CONCAT_WS(
', ', count(DISTINCT CONCAT(q.emailaddress, '_', e.id)),
dv.OneTimeItems
) as AllItems,
e.id,
LEFT(e.firstname, 1) as voorletter,
e.lastname
FROM question q
LEFT JOIN employee e ON q.employee_id = e.id
LEFT JOIN
(
SELECT company_id,
GROUP_CONCAT(items SEPARATOR '; ') AS OneTimeItems
FROM (
SELECT oi.company_id,
CONCAT_WS(
': ', oi.item_name, SUM(oi.item_amount)
) items
FROM onetime_item oi
WHERE oi.date BETWEEN '2015-12-01'
AND LAST_DAY('2015-12-01')
GROUP BY oi.company_id, oi.item_name
) resulta
GROUP BY company_id
) AS dv
ON dv.company_id = e.company_id
WHERE 1=1
AND YEAR(q.created_at) = '2015'
AND MONTH(q.created_at) = '12'
GROUP BY e.company_id ;
Test in SQLfiddle.
Unrelated to the issue comments:
There is GROUP BY e.company_id while the select list has e.id, LEFT(e.firstname, 1), e.lastname. All these will give arbitrary result from a (more or less random) employee for each company - or even in extremely rare cases arbitrary results from 2 or 3 different employees! MySQL allowed (before 5.7) such bad use of group by that could cause erroneous results. It has been fixed in 5.7 and the default settings would reject this query.
The condition:
YEAR(created_at) = '2015' AND MONTH(created_at) = '12'
cannot make use of indexes. It's better to rewrite with either BETWEEN if the column is of DATE type of with an inclusive-exclusive range condition, which works flawlessly with any datetime type (DATE, DATETIME, TIMESTAMP) of any precision:
-- use only when the type is DATE only
date BETWEEN '2015-12-01' AND LAST_DAY('2015-12-01')
or:
-- use when the type is DATE, DATETIME or TIMESTAMP
created_at >= '2015-12-01' AND created_at < '2016-01-01'
I know similar questions have been presented a few times, but it seems these all related to the field value changing based on a specific field value. I actually want to join a completely different table based on the the field value of each record.
I think this is possible with a single query, but since the join table could change every record I'm not sure.
To clarify I have four tables; I am trying to join three of them in a single query. However the third table could be one of two tables depending on the value in each records field.
I would imagine it would look something like this, but its not working.
SELECT crm_deal . * , crm_deal_step.sort_order, COALESCE( CONCAT_WS(' ',crm_lead.first_name, crm_lead.last_name),CONCAT_WS(' ',customer.firstname, customer.lastname) ) AS contact_full_name
FROM `crm_deal`
IF( crm_deal.deal_for = 'lead', LEFT JOIN crm_lead ON crm_deal.deal_for_id = crm_lead.lead_id, LEFT JOIN customer ON crm_deal.deal_for_id = customer.customer_id)
LEFT JOIN crm_deal_step ON crm_deal.deal_step_id = crm_deal_step.deal_step_id
Am I close? Is it possible or do I need to do a php loop and run a second query for every record?
You cant use if stat ment in a select statment.
But you can try this:
SELECT crm_deal . * , crm_deal_step.sort_order,
CASE WHEN crm_lead.first_name is not null AND
crm_lead.last_name is not null
THEN crm_lead.first_name+ ' '+crm_lead.last_name
ELSE customer.firstname +' '+ customer.lastname
END as contact_full_name
FROM `crm_deal`
LEFT JOIN crm_lead ON crm_deal.deal_for_id = crm_lead.lead_id AND crm_deal.deal_for = 'lead'
LEFT JOIN customer ON crm_deal.deal_for_id = customer.customer_id AND crm_deal.deal_for <> 'lead'
LEFT JOIN crm_deal_step ON crm_deal.deal_step_id = crm_deal_step.deal_step_id
I have this MySQL query to get the total amount of only the first invoice for each client on a given month:
SELECT SUM(InvoiceProductTotal)
FROM tblinvoiceproduct
WHERE InvoiceID IN (
SELECT MIN(tblinvoice.InvoiceID) AS InvoiceID
FROM tblinvoice
WHERE tblinvoice.ClientID IN (
SELECT tblclient.ClientID
FROM tblclient
LEFT JOIN tblenquiry ON tblclient.EnquiryID = tblenquiry.EnquiryID
WHERE NOT tblclient.EnquiryID IS NULL
AND YEAR(EnquiryDate) = 2014
AND MONTH(EnquiryDate) = 9
)
GROUP BY tblinvoice.ClientID
);
When I run it, it seems to loop forever. If I remove the first part it gives me the list of invoices instantly. Am sure it is a small syntax detail but haven't been able to figure out what the problem is after nearly one hour trying to fix it.
Your assistance is appreciated.
This query can probably be done in a better way without all the sub queries as well, just I'm not so experienced with sub queries. :)
Solution was given but I should have included the full query rather than just the part I was having trouble with. The full query is:
SELECT AdvertisingID, AdvertisingTitle, AdvertisingYear,
AdvertisingMonth, AdvertisingTotal, AdvertisingVisitors,
IFNULL(
(SELECT SUM(InvoiceProductTotal)
FROM tblinvoiceproduct
JOIN
(SELECT MIN(tblinvoice.InvoiceID) AS InvoiceID
FROM tblinvoice
JOIN
(SELECT DISTINCT tblclient.ClientID
FROM tblclient
JOIN tblenquiry ON tblclient.EnquiryID = tblenquiry.EnquiryID
WHERE YEAR(tblenquiry.EnquiryDate)=tbladvertising.AdvertisingYear
AND MONTH(tblenquiry.EnquiryDate)=tbladvertising.AdvertisingMonth)
AS inq
ON tblinvoice.ClientID = inq.ClientID
GROUP BY tblinvoice.ClientID) AS inq2
ON tblinvoiceproduct.InvoiceID = inq2.InvoiceID)
, 0)
FROM tbladvertising
ORDER BY AdvertisingYear DESC, AdvertisingMonth DESC, AdvertisingTitle;
Now the problem is that the column with the sub query has no access to "tbladvertising.AdvertisingYear" or "tbladvertising.AdvertisingMonth"
A commenter mentioned that it's hard to understand what you're trying to do here. I agree. But I will take the risk of trying to puzzle it out.
As usual with this sort of query, it's helpful to take advantage of the structured part of structured query language, and try to build this up piece by piece. That's the secret to creating complex queries that actually do what you want them to do.
Your innermost query is this:
SELECT tblclient.ClientID
FROM tblclient
LEFT JOIN tblenquiry ON tblclient.EnquiryID = tblenquiry.EnquiryID
WHERE NOT tblclient.EnquiryID IS NULL
AND YEAR(EnquiryDate) = 2014
AND MONTH(EnquiryDate) = 9
It is saying, "give me the list of ClientID values which have enquiries in September 2014. There's a more efficient way to do this:
SELECT DISTINCT tblclient.ClientID
FROM tblclient
JOIN tblenquiry ON tblclient.EnquiryID = tblenquiry.EnquiryID
WHERE tblenquiry.EnquiryDate >= '2014-09-01'
AND tblenquiry.EnquiryDate < '2014-09-01' + INTERVAL 1 MONTH
Two changes here: First, the NOT ... IS NULL search is unnecessary because if the item you're searching on is null, there's no way for your EnquiryDate to be valid. So we just change the LEFT JOIN to an ordinary inner JOIN and get rid of the otherwise expensive NULL scan.
Second, we recast the date matching as a range scan, so it can use an index on tbl.EnquiryDate.
Cool.
Next, we have this query level.
SELECT MIN(tblinvoice.InvoiceID) AS InvoiceID
FROM tblinvoice
WHERE tblinvoice.ClientID IN (
/* that list of Client IDs from the innermost query */
)
GROUP BY tblinvoice.ClientID
That is pretty straightforward. But MySQL isn't too swift with IN () clauses, so let's recast it in the form of a JOIN as follows:
SELECT MIN(tblinvoice.InvoiceID) AS InvoiceID
FROM tblinvoice
JOIN (
/* that list of Client IDs from the innermost query */
) AS inq ON tblinvoice.ClientID = inq.ClientID
GROUP BY tblinvoice.ClientID
This gets us the list of invoice IDs which were the subject of the first enquiry of the month on behalf of each distinct ClientID. (It's hard for me to figure out the business meaning of this, but I don't understand your business.)
Finally, we come to your outermost query. We can also recast that as a JOIN, like so.
SELECT SUM(InvoiceProductTotal)
FROM tblinvoiceproduct
JOIN (
/* that list of first-in-month invoices */
) AS inq2 ON tblinvoiceproduct.InvoiceID = inq2.InvoiceID
So, this all expands to:
SELECT SUM(InvoiceProductTotal)
FROM tblinvoiceproduct
JOIN (
SELECT MIN(tblinvoice.InvoiceID) AS InvoiceID
FROM tblinvoice
JOIN (
SELECT DISTINCT tblclient.ClientID
FROM tblclient
JOIN tblenquiry ON tblclient.EnquiryID = tblenquiry.EnquiryID
WHERE tblenquiry.EnquiryDate >= '2014-09-01'
AND tblenquiry.EnquiryDate < '2014-09-01' + INTERVAL 1 MONTH
) AS inq ON tblinvoice.ClientID = inq.ClientID
GROUP BY tblinvoice.ClientID
) AS inq2 ON tblinvoiceproduct.InvoiceID = inq2.InvoiceID
That should do the trick for you. In summary, the big optimizing changes are
using a date range scan.
eliminating the NOT ... IS NULL criterion.
recasting your IN clauses as JOIN clauses.
The next step will be to create useful indexes. A compound index (EnquiryDate, EnquiryID) on your tblenquiry is very likely to help a lot. But to be sure you'll need to do some EXPLAIN analysis.
What if you modify your above posted query, to replace the subquery with JOIN (INNER JOIN) like below. Give it a try.
SELECT SUM(InvoiceProductTotal)
FROM tblinvoiceproduct
JOIN
(
SELECT MIN(ti.InvoiceID) as MinInvoice
FROM tblinvoice ti
JOIN
(
SELECT tblclient.ClientID
FROM tblclient
LEFT JOIN tblenquiry
ON tblclient.EnquiryID = tblenquiry.EnquiryID
WHERE NOT tblclient.EnquiryID IS NULL
AND YEAR(EnquiryDate) = 2014
AND MONTH(EnquiryDate) = 9
) tab
on ti.ClientID = tab.ClientID
GROUP BY ti.ClientID
) tab1
on tblinvoiceproduct.InvoiceID = tab1.MinInvoice