I have a query that calculates information for a revolving monthly retainer. The Project has a certain number of hours assigned to it each monthly period, with periods starting at different times of the month (e.g., February 5th to March 4th). The columns of the query result include:
Project Name
Total Hours Logged
Monthly Hours Remaining
Last Day of Period
Days Remaining
For example, Project A has 15 hours logged to it, with 5 hours remaining in the monthly period. The last day of the period is November 17th, with 3 days remaining from today.
The current Query takes 4 tables that are joined using a left join to print all of the Clients even if there are no hours logged, then it uses a nested subquery (lines 8-86) to calculate columns 2, 3, 4, and 5. However, column 4 and 5 do not print, they just show as always NULL. (Those are Last Day of Period and Days Remaining).
You can see the schema + query code at the SQL fiddle link here: http://sqlfiddle.com/#!2/fc830/12
How can I get column 4 and 5 to print the data and not be null when there are no Hours Logged? I think I may need an additional left join but I am not able to get a solution. If you have any suggestions it would be appreciated. Thanks!
When there has been no hours logged, the left join to the nested query returns nulls for the columns you're having trouble with.
The answer is to provide values for them when they are null and us ifnull() to use those value when the left join returns nulls:
select
...
ifnull(<left joined value>, <value when there's no join>),
...
See the working solution in sqlfiddle.
Incidentally, some values you are returning from the inner query, particularly the "last day" value can be derived directly from client (as seen in my solution). It's best to keep your queries as simple as possible - don't get data in a complicated way when there's an simpler or more direct way.
I think you are not thorough with the idea of LEFT JOIN. LEFT JOIN results in all rows from the outer table. Its otherwise called LEFT Outer JOIN (contrary to RIGHT OUTER JOIN). In your case the inner query results in only record with client id = 4. See the last clause X on X.id=client.id. Now what do you expect the results to be?
OUTER TABLE (client table)
id = 1, 2, 3, 4
INNER TABLE
client id = 4
ON X.id=client.id
An INNER JOIN would result in just one record since there is only matching record - for id = 4.
But a LEFT JOIN would result in all 4 records from outer table but the values for invalid fields will be NULL. Here except for client id 4, there is no valid records from inner table, hence they will be null.
To get more clarity you will have to see the id field along the records. Try this fiddle
You can see the other answer as to how to fill those NULL fields..
Related
I'm doing a left join on a table to get the number of leads we've generated today and how many times we've called those leads. I figured a left join would be the best thing to do, so I wrote the following query:
SELECT
COUNT(rad.phone_number) as lead_number, rals.lead_source_name as source, COUNT(racl.phone_number) as calls, SUM(case when racl.contacted = 1 then 1 else 0 end) as contacted
FROM reporting_app_data rad
LEFT JOIN reporting_app_call_logs racl ON rad.phone_number = racl.phone_number, reporting_app_lead_sources rals
WHERE DATE(rad.created_at) = CURDATE() AND rals.list_id = rad.lead_source
GROUP BY rad.lead_source;
But the problem with that, is that if in the reporting_app_call_logs table, there are multiple entries for the same phone number (so a phone number has been called multiple times that day), the lead_number (which I want to count how many leads were generated on the current day grouped by lead_source) equals how many calls there are. So the count from the LEFT table equals the count from the RIGHT table.
How do I write a SQL query that gets the number of leads and the total number of calls per lead source?
Try COUNT(DISTINCT expression)
In other words, change COUNT(rad.phone_number) to COUNT(DISTINCT rad.phone_number)
I've a table with a structure something like this,
Device | paid | time
abc 1 2 days ago
abc 0 1 day ago
abc 0 5 mins ago
Is it possible to write a query that checks the paid column on all the rows where Device = abc and then outputs the most recent two rows that different. Basically, something like an if statement saying if row 1 = 1 and row 2 = 0 output that but only if it's the most recent two columns that are different. For example, in this case, the first and second row. The table is being updated whenever a user changes from a free to paid account etc. It is also updated in different columns for different reasons hence the duplicate 0s for example.
I know this would probably be done better by having another table altogether and updating that every time the user switches account type, but is there any way to make this work?
Thanks
Example:
http://rextester.com/MABU7860 need further testing on edge cases but this seems to work.
SELECT A.*, B.*
FROM SQLfoo A
INNER JOIN SQLFoo B
on A.Device = B.Device
and A.mTime < B.mTime
WHERE A.Paid <> B.Paid
and A.device = 'abc'
ORDER BY B.mTime Desc, A.MTime Desc
LIMIT 1
By performing a self join we on the devices where the time from one table is less than the time from the next table (thus the two records will never matach and we only get the reuslts one way) and we order by those times descending, the highest times appear first in the result since we limit by a single device we don't need to concern ourselves with the devices. We then just need compare the paid from one source to the paid in the 2nd source and return the first result encountered thus limit 1.
Or using user variables
http://rextester.com/TWVEVX7830
in other engines one might accomplish this task by performing the join as in above, assigning a row number partitioned by the device and then simply return all those row_numbers with a value of 1; which would be the earliest date discrepency.
Use LIMIT to limit the number of record on mysql:
http://www.mysqltutorial.org/mysql-limit.aspx
In your case, use LIMIT 2
and then put the 2 record that you just select into an array, then compare the array if the value is different. If they are different then print
There's a lot of these issues floating around the net with many solutions, but I'm really struggling with this one.
I have a table [BaseHrs] which looks a little like this -
p_ID b_Person WeekNos HrsRequired
1 A 2016-39 10
1 A 2016-40 10
1 A 2016-41 10
1 A 2016-42 10
1 B 2016-39 11
1 B 2016-40 11
1 B 2016-41 12
1 B 2016-42 09
The table continues with different p_ID, people & week numbers. There is no Primary Key and no indexing. This table also has no relationship with any other table.
It is populated from a Query connected to another table as well as a form for the [HrsRequired] field.
Scenario -
Project 1 (p_ID=1) has now been brought forward by two weeks and BaseHrs table no longer needs row for [WeekNos] 2016-41 & 2016-42.
I initially use a query to show which weeks the project is now running on (qry_SelectNewDates).
I have started my delete query by first creating a Select query which looks like this -
SELECT BaseHrs.*
FROM BaseHrs
LEFT JOIN qry_SelectNewDates
ON BaseHrs.WeekNos = qry_SelectNewDates.WeekNos
WHERE (((BaseHrs.p_ID)=[Forms]![frm_Projects]![p_ID])
AND ((BaseHrs.WeekNos) Not In ([qry_SelectNewDates].[WeekNos])));
This works as intended.
Converting that into a delete query produces an error though. Delete Query -
DELETE BaseHrs.*, BaseHrs.p_ID, BaseHrs.WeekNos
FROM BaseHrs
LEFT JOIN qry_SelectNewDates
ON BaseHrs.WeekNos = qry_SelectNewDates.WeekNos
WHERE (((BaseHrs.p_ID)=[Forms]![frm_Projects]![p_ID])
AND ((BaseHrs.WeekNos) Not In ([qry_SelectNewDates].[WeekNos])));
Error message -
Could not delete from specified tables.
I realise that there is often an issue when trying to delete records in this way. I've tried using it with just 'DELETE.*' in the first line without luck.
I have also made an attempt at a nested Query, but I just can't figure out how to construct it. Any guidance?
**********EDIT**********
With advice from #SunKnight0 I have added a primary key to my BaseHrs table and got this query -
DELETE *
FROM BaseHrs
WHERE b_pKey IN
(SELECT BaseHrs.b_pKey
FROM BaseHrs
LEFT JOIN qry_SelectNewDates
ON (BaseHrs.WeekNos = qry_SelectNewDates.WeekNos)
WHERE (((BaseHrs.p_ID)=[Forms]![frm_Projects]![p_ID])
AND ((BaseHrs.WeekNos) Not In ([qry_SelectNewDates].[WeekNos]))));
This query appears to work but takes a huge amount of time to run. Is that as good as it gets?
Im breaking my head with this little query so hopefully someone can tell me what is wrong with it. I need to group orders by same month and year as well as order status. So I have different table with which matches a number let's say 4 to an order status in text (let's say DELIVERED). I am trying get mySQL to tell me out of each Month/Year how many orders it had in every given status (even if that number is zero). Up to this point I receive the right answer except for the months/year that have zero occurences. So I only see for example: 05/205 | DELIVERED | 45, but if there is for example zero "PENDING" orders for 05/25 I do not see 05/2015 | PENDING | 0 and that is what i'm looking for. Thanks a bunch guys
select order_statuses.status, order_statuses.status_text,
Month(str_to_date(orders.order_date, '%d/%c/%y')) as Good_Month,
Year(str_to_date(orders.order_date, '%d/%c/%y')) as Good_Year,
CONCAT(Month(str_to_date(orders.order_date, '%d/%c/%y')), "/",Year(str_to_date(orders.order_date, '%d/%c/%y'))) as The_Date
from order_statuses
left outer join orders
on order_statuses.`status` = orders.order_status
where orders.admin_comments like '%moveit.ca%'
group by Good_Year, Good_Month, order_statuses.`status`
I hope I will be able to make my problem clear.
Ik have a table called tweets from which I want to extract information for each data in the daterange table. This table holds 142 dates, of which 102 dates have the property trading (day on which market was open) set to 1 (trading=1).
The below query extracts information from the tweets table for 20 companies (identified by sp100_id). The expected resultset therefore contains 20 x 102 = 2,040 rows. However, I only get returned 1,987 rows because for some date-company combinations, the tweets table holds no data. I need these "empty days" to be included in the resultset however. I thought I could accomplish this by using COALESCE(X, 0), returning 0 if there would be no data, but the result is the same: 1,987 rows.
Based on this information and the query below, does anybody know how I can get it to return 102 rows (1 row for each daterange._date with trading=1) for each sp100_id in the tweets table?
SELECT
sp100.sp100_id,
daterange._date,
COALESCE(SUM(IF(tweets.classify1=2, tweets.`retweet_count`, 0)),0) AS `pos-retweet`,
COALESCE(SUM(IF(tweets.classify1=2, tweets.`user-quality`, 0)),0) AS `pos-quality`,
COALESCE(SUM(IF(tweets.classify1=2, tweets.`follow`, 0)),0) AS `pos-follow`,
COALESCE(SUM(IF(tweets.classify1=3, tweets.`retweet_count`, 0)),0) AS `neg-retweet`,
COALESCE(SUM(IF(tweets.classify1=3, tweets.`user-quality`, 0)),0) AS `neg-quality`,
COALESCE(SUM(IF(tweets.classify1=3, tweets.`follow`, 0)),0) AS `neg-follow`
FROM
sp100
CROSS JOIN
daterange
LEFT JOIN
tweets
ON tweets.nyse_date = daterange._date
AND tweets.sp100_id = sp100.sp100_id
WHERE sp100.sp100_id BETWEEN 1 AND 20 AND tweets.type != 1 AND daterange.trading = 1
GROUP BY
sp100.sp100_id, daterange._date
In any other case, I would provide you with a SQLFiddle, but it would be a lot of work to export a proper portion of the tables used to SQLFiddle while the solution might be clear to some real SQL guru anyway :-)
The problem comes from requiring that tweets.type != 1 in your WHERE clause.
For the dates that have no associated tweets, the outer join will result in all tweets columns, including tweets.type, being NULL. As documented under Working with NULL Values:
Because the result of any arithmetic comparison with NULL is also NULL, you cannot obtain any meaningful results from such comparisons.
In MySQL, 0 or NULL means false and anything else means true. The default truth value from a boolean operation is 1.
Therefore such records are filtered by your WHERE clause.
As #Martin Smith commented, you can move this filter criterion into the ON clause of your outer join (so that the test is performed only against actual tweets records rather than simulated NULL ones).
Alternatively, you could rewrite the filter to handle NULL. For example, using the NULL-safe equality operator:
NOT tweets.type <=> 1
As an aside, I usually don't bother with a daterange table and instead omit dates for which there is no data from the resultset: instead, I handle missing dates within my application code.
You need a calendar table filled with each day. I know it might sound silly, but this solution solves yo a lot of problems. The same solution you can have also with integers ( integer tables)