Summary
I am attempting to LEFT JOIN on a filtered TABLE in SQL.
To do so I am first declaring a temporary table in an # variable, then attempting to join on it later.
Unfortunately I am not having much luck doing this, it massively speeds up my query when limiting to such a resultset.
Other Routes Tried
I initially was trying to conduct this on the WHERE, however this was preventing rows from occurring where there were no matching events (I am counting the events by intervals_days so I need to return regardless of whether there's matching in the other).
After I realised my mistake, I moved to the ON. I have not seen examples of this done, and I have a feeling doing a FIND_IN_SET here on a SELECT would not be performant?
QUERIES
The Initial Filter
DECLARE #filtered_events TABLE (id INT, eventable_id INT, eventable_type VARCHAR(255), occurred_at TIMESTAMP, finished_at TIMESTAMP)
INSERT INTO #filtered_events
SELECT
e.id, e.eventable_id, e.eventable_type, e.occurred_at, e.finished_at
FROM
units_events as ue
LEFT JOIN
events as e
ON
e.id = ue.event_id
WHERE
ue.unit_id
IN
(1,2,3);
Ignore the (1,2,3) here, these values are added to the query dynamically.
My Attempted Use Of It
SELECT
i.starts_at as starts_at,
i.ends_at as ends_at,
count(fe.id) as count
FROM
intervals_days as i
LEFT JOIN
#filtered_events as fe
ON
( fe.occurred_at >= starts_at AND fe.occurred_at < ends_at )
WHERE
( starts_at >= '15-01-2019' AND ends_at < NOW() )
GROUP BY
starts_at
ORDER BY
starts_at
DESC;
These queries are within one SQL document, one above the other with the terminating ; semicolon on each.
I expect this to output what my lower query outputs (a grouped resultset by the intervals_days rows)- however with the benefit of my LEFT JOIN query being conducted on a much smaller sample.
Sorry, no such syntax.
MySQL has no concept of arrays, either.
Nor can you use DECLARE outside of a Stored Routine.
As Shadow said in the comments, with MySQL I would use a subquery like this:
SELECT
i.starts_at as starts_at,
i.ends_at as ends_at,
count(fe.id) as count
FROM
intervals_days as i
LEFT JOIN
(
SELECT
e.id,
e.eventable_id,
e.eventable_type,
e.occurred_at,
e.finished_at
FROM units_events as ue
LEFT JOIN events as e
ON e.id = ue.event_id
WHERE ue.unit_id IN (1,2,3)
) fe
ON ( fe.occurred_at >= starts_at AND fe.occurred_at < ends_at )
WHERE ( starts_at >= '15-01-2019' AND ends_at < NOW() )
GROUP BY starts_at
ORDER BY starts_at
DESC;
Related
I have a MySQL table which stores the data of a hotel's reservations.
I need a query to see the amount of guests who stayed in the hotel for each date.
I was able to create a query (using a subquery) but it performs very slowly. Is there a better way to get the requested data? (For example join the table to itself, or whatever.)
My query is:
SELECT CheckOutDate AS Date,
(SELECT SUM(NrOfGuests) FROM tblGuests tG
WHERE tG.CheckInDate <= tblGuests.CheckOutDate
AND tG.CheckOutDate > tblGuests.CheckOutDate
AND tG.IsCancelled = False AND tG.NoShow = False)
AS NrOfGestsStaying
FROM tblGuests
GROUP BY CheckOutDate
What is the best way to make it perform faster?
In the original query, the SELECT returns a SUM on every row of the table using a subquery. The duplicates are removed afterwards using a group by CheckOutDate. So, in other words, this is the SUM(NrOfGuests) for distinct CheckOutDate.
You can remove duplicate CheckOutDate in advance by subquerying distinct CheckOutDate. So in the receiving query the SUM is applied just one time for distinct CheckOutDate:
SELECT dT.CheckOutDate
,(SELECT SUM(NrOfGuests)
FROM tblGuests tG
WHERE tG.CheckInDate <= dT.CheckOutDate
AND tG.CheckOutDate >= dT.CheckOutDate
AND tG.IsCancelled = 0
AND tG.NoShow = 0
) AS NrOfGuests
FROM (
SELECT DISTINCT CheckOutDate
FROM tblGuests
) AS dT
ORDER BY dT.CheckOutDate
I have a table where I am storing the stored number of barrels inside of many tanks. I am storing values here every night at midnight, and at the beggining and end of any operator initiated transfer.
What I want to return is the number of barrels difference since the previous event record for that specific tank. I have the correct ID for the self join to get the previous record number, however the barrels is incorrect.
Here is what I currently have.
SELECT
inventory.id,
MAX(inventory2.id) AS id2,
inventory.tankname,
inventory.barrels,
inventory.eventstamp,
inventory2.barrels
FROM
inventory
LEFT JOIN
inventory inventory2 ON inventory2.tankname = inventory.tankname AND inventory2.eventstamp < inventory.eventstamp
GROUP BY
inventory.id,
inventory.tankname,
inventory.barrels,
inventory.eventstamp
ORDER BY
inventory.tankname,
inventory.eventstamp
That returns the following
Just use correlated subqueries:
SELECT i.*,
(SELECT i2.id
FROM inventory i2
WHERE i2.tankname = i.tankname AND
i2.eventstamp < i.eventstamp
ORDER BY i2.eventstamp DESC
LIMIT 1
) as prev_id,
(SELECT i2.barrels
FROM inventory i2
WHERE i2.tankname = i.tankname AND
i2.eventstamp < i.eventstamp
ORDER BY i2.eventstamp DESC
LIMIT 1
) as prev_barrels
FROM inventory i
ORDER BY i.tankname, i.eventstamp;
Your query doesn't work because you have columns in the SELECT that are not in the GROUP BY and are not aggregated. That shouldn't be allowed in any database; it is unfortunate that MySQL does allow it.
I am trying to run the following query to obtain the sales for each type of job for a particular period. However for certain months where there are no jobs of a particular job type performed no 0 is displayed in sales.
How can i display the zeros in such a condition.
Here is the sql query-
select Year(postedOn), month(postedOn), jobType, sum(price)
from tbl_jobs
group by jobType, year(postedOn), month(postedOn)
order by jobType, year(postedOn), month(postedOn)
Typically, this is where your all-purpose calendar or numbers table comes in to anchor the query with a consistent sequential set:
SELECT job_summary.*
FROM Calendar
CROSS JOIN (
-- you may not have though about this part of the problem, though
-- what about years/months with missing job types?
SELECT distinct jobType FROM tbl_jobs
) AS job_types
LEFT JOIN (
select Year(postedOn) AS year,month(postedOn) as month,jobType ,sum(price)
from tbl_jobs
group by jobType, year(postedOn), month(postedOn)
) job_summary
ON job_summary.jobType = job_types.jobType
AND job_summary.year = Calendar.year
AND job_summary.month = Calendar.month
WHERE Calendar.day = 1 -- Assuming your calendar is every day
AND calendar.date BETWEEN some_range_goes_here -- you don't want all time, right?
order by job_types.jobType, Calendar.year, Calendar.month
I have somehow complicated query as follow:
SELECT w,e2.EntityID
FROM (SELECT EntityID,SUM(frequency) w
FROM omid.entity_epoch_data where EpochID in
( select id from epoch where startDateTime>='2013-11-01 00:00:00' and
startDateTime <= '2013-11-30 00:00:00')
GROUP BY EntityID) e1
RIGHT JOIN
entity e2 ON e1.EntityID = e2.EntityID order by w desc
And it works properly but as soon as I add another innerjoin:
SELECT w,e2.EntityID
FROM (SELECT EntityID,SUM(frequency) w
FROM omid.entity_epoch_data inner join omid.entity_dataitem_relation as e3 on(e1.EntityID = e3.EntityID)
where e3.dataitemtype=3 and EpochID in
( select id from epoch where startDateTime>='2013-11-01 00:00:00' and
startDateTime <= '2013-11-30 00:00:00')
GROUP BY EntityID) e1
RIGHT JOIN
entity e2 ON e1.EntityID = e2.EntityID order by w desc
I get the following error:
column 'EntityID' in the field set is ambiguous
Does anyone have an idea where my mistake is?
Update :
I have the right version as follow (it gives me exactly what I want)
SELECT ID,e2.EntityID,e2.Name,AVG(intensity),AVG(quality),SUM(frequency)
FROM omid.entity_epoch_data as e1
right JOIN entity AS e2 ON (e1.EntityID = e2.EntityID)
where EpochID in
( select id from epoch where startDateTime>='2013-11-01 00:00:00' and
startDateTime <= '2013-11-30 00:00:00') and e1.EntityID in
(SELECT entityid FROM omid.entity_dataitem_relation where dataitemtype=3)
group by e2.EntityID order by sum(frequency) desc;
But it takes time and I need to change the
(SELECT entityid FROM omid.entity_dataitem_relation where dataitemtype=3)
group by e2.EntityID order by sum(frequency) desc;
to innerjoin can anyone help me how to do that?
Image:
You have two problems.
First off, the syntax for that first INNER JOIN is not correct. You are conflating the INNER JOIN and ON parts of the statement. (At least, I think this is the case, unless you actually have "." characters in your table names are really want a cartesian result from this JOIN)>
Secondly, the second usage of EntityID (in that new INNER JOIN you added) could refer to the EntityID value from either the left or right side of the INNER JOIN -- that's what the "ambiguous column" in the error means.
Here is some SQL to get you started. It follows the selection logic you give in the comment, below. It names each table with an alias and ensures that each column specifies the table from which it should be drawn. I don't have an instance of MySQL handy to try it, but it should get you started.
SELECT E.EntityID, SUM(EED.Frequency)
FROM entity_epoch_data EED INNER JOIN entity_dataitem_relation EDR
ON EED.EntityID = EDR.EntityID WHERE EDR.entity_dataitemtype = 3
RIGHT OUTER JOIN entity E ON E.EntityID = EED.EntityID
GROUP BY E.EntityID
Maybe there are fields named EntityID in both tables - omid.entity_epoch_data and omid.entity_dataitem_relation ?
Your first query had only one take in the subquery to look for entityid, but the second query has two tables in the subquery.
Also, you are referencing table e1 inside a subquery before e1 is defined.
give the tables a specific alias name and that error will go away.. because you have two columns that are not distinguished, so when you try to use one it doesn't know which table to pull it from
I'm attempting to condense several SQL calls into one, and wanted to know if the following were possible.
Here are are my tables.
Organization
id
Event
id,
startDate
endDate
FavoriteOrgs
userId
orgId
For each day of the month, I want to return a count of the events occurring on that day. Not too difficult, until you add the fact that events can span 2 or 3 days.
Here's what I have so far, which accurately shows event counts by day, but it only includes the event in count for the day it begins.
SELECT DAYOFMONTH( CAST( o.start_date AS DATE ) ) AS dayNum, COUNT( * ) AS count
FROM favoriteOrgs f, event e, organization o
WHERE f.user_id =200372
AND e.profile_id = o.id AND e.profile_id = f.profile_id AND o.id = f.profile_id
AND e.last_date >= '$startDate'
AND e.start_date <= '$lastDate'
GROUP BY e.start_date
Your data model isn't actually modeling all of the entities in your system. One entity is the dates for which you are interested in reporting things.
You should add a table to your database (or use a temporary table, inline table, or whatever else is available with MySQL). The table is simply all of the relevant dates. Even a table that goes back to 1900 and forward to 2100 is going to be fairly small.
The query then becomes trivial:
CREATE TABLE Calendar (
calendar_date DATE NOT NULL,
CONSTRAINT PK_Calendar PRIMARY KEY CLUSTERED
)
SELECT
C.calendar_date,
COUNT(*) AS count
FROM
favoriteOrgs F
INNER JOIN Event E ON
E.profile_id = F.profile_id AND
E.last_date >= '$startDate' AND
E.start_date <= '$lastDate'
INNER JOIN Organization O ON
O.id = F.profile_id AND
O.id = E.profile_id
WHERE
F.user_id = 200372
GROUP BY
C.calendar_date
You also now have an advantage that you can add additional business specific information to calendar dates, like an "is_holiday" column, "economic_quarter" or whatever. Just remember to prefill the table, which is a simple loop that you have to run once.