For implicity, I am going to say we have 5 tables for SQL
1) Client Accounts
2) Leads
3) Calls
4) Report
Below is a SQL example I made up to get an idea how it works. My question is, how can I retrieve records that received a business report from date of the phone calls and 5 days onwards.
Example
Today is 8.06.2017, I made a phone call to a Lead. This lead is ABC. ABC is interested in XYZ. I sent a report to XYZ on 12.06.17. This is within 5 days. So this record should be retrieved and counted as I potentially find more than 100 records within 5 days.
SELECT Id, Name, DateOfCall
FROM Lead, Call, ClientAccounts
ON Lead.id = Call.id AND Lead.id = ClientAccounts.id
WHERE DATEDIFF (DateOfReport, StartOfCall, getDate()) < 5
If I execute this Sql statement, it should retrieve the above report, the name of ClientAccount and the Lead associated with that ClientAccount.
I hope this make sense and I am very beginner in SQL, but I am aware my syntaxes for SQL is wrong but the idea is understanding this and worry about syntax a little later as I do not have SQL server to execute this.
You aren't far off from your goal.
You need to use proper JOIN syntax; what you have is a bit fractured.
SELECT ... some columns ...
FROM Lead L
JOIN Call C ON L.id = C.id
JOIN ClientAccounts CA ON L.id = CA.id
That assigns the alias names L, C, and CA to your three tables.
You can try running this with SELECT * to see what you get. But using * in a finished query is a rookie mistake.
Next you need to give the names, with table aliases, of the columns you wish to SELECT. I have to guess which table each column comes from, because you didn't show us your table definitions. So, I guess...
SELECT L.id, L.Name, C.DateOfCall
FROM Lead L
JOIN Call C ON L.id = C.id
JOIN ClientAccounts CA ON L.id = CA.id
Run that. See what you get. If my example has the table or column names wrong, fix them. (Please don't ask us to fix them here on SO; we don't know your table layouts.)
Finally you need to select the date range. Try
WHERE C.StartOfCall >= CURDATE() - INTERVAL 5 DAY
This will get all rows where the call started on or after midnight five days ago.
Related
I've never been able to get my head around INNER JOINs (or any other JOIN types for that matter) so I'm struggling to work out how to use it in my specific situation. In fact, I'm not even sure if it's what I need. I've looked at other examples and read tutorials but my brain just doesn't seem to work the way needed to truly get it (or it doesn't function at all).
Here's the scenario:
I have two tables -
phone_numbers - this table has a list of phone numbers that
belong to lots of different customers. A single customer can have
multiple numbers. For simplicity's sake, we'll say the fields are
'number_id', 'customer_id', 'phone_number'.
call_history - this table has a record of every single call that one of these
numbers in the first table could have had. There's a record for
every individual call going back years. Again, for simplicity,
we'll say the relevant fields are customer_id, phone_number,
call_start_time.
What I'm trying to accomplish is to find all of the numbers that belong to a particular customer_id in the phone numbers table and use that information to search through the call_history table and find the number of calls each phone number has received, and group that by the number of calls for each number, preferably also showing zeros where a number hasn't received any calls at all.
The reason the zero calls is important is because that's the data I'm interested in. Otherwise, I could just get all the information out of the call_history table. But what I'm trying to achieve is find the numbers with no activity.
All I've been able to accomplish is run one query to get all of the numbers belonging to one customer:
SELECT customer_id, phone_number FROM phone_numbers WHERE customer_id = Y;
Then run a second query to get all phone calls for that customer_id for a set duration:
SELECT customer_id, phone_number, COUNT(*) FROM call_history WHERE customer_id = Y and call_start_time >= DATE_SUB(SYSDATE(), INTERVAL 30 DAY) GROUP BY phone_number;
I've then had to use the data returned from both queries and use a VLOOKUP function in Excel to match number of calls for each individual number from the second query to the list of all numbers from the first query, thus leaving blanks in my "all numbers" table and identifying those numbers that had no calls for that time period.
I'm hoping there's some way to do all of this with a single query and return a table of results, listing the zero number of calls with it and eliminate the whole manual Excel bit as it's not overly efficient and prone to human error.
Without at least a workable example from you, it's not easy to re-create your situation. Anyway, INNER JOIN might not return the result as how you expected. In my short time with MySQL, I mainly use 2 types of JOIN; one is already mentioned and the other is LEFT JOIN. From what I can understand in your question, what you want to achieve can be done by using LEFT JOIN instead of INNER JOIN. I may not be the best person to explain this to you but this is how I understand it:
INNER JOIN - only return anything that match in ON clause between two (or more) tables.
LEFT JOIN - will return everything from the table on the left side of the join and return NULL if ON get no match in the table on the right side of the join .. unless you specify some WHERE condition from something on the right table.
Now, here is my query suggestion and hopefully it'll be useful for you:
SELECT A.customer_id, A.phone_number,
SUM(CASE WHEN call_start_time >= DATE_SUB(SYSDATE(), INTERVAL 30 DAY)
THEN 1 ELSE 0 END) AS Total
FROM phone_numbers A
LEFT JOIN call_history B
ON A.customer_id=B.customer_id
GROUP BY A.customer_id,A.phone_number;
What I did here is I LEFT JOIN phone_numbers table with call_history on customer_id and I re-position the WHERE call_start_time >= .. condition into a CASE expression in the SELECT since putting it at WHERE will turn this into a normal join or inner join instead.
Here is an example fiddle : https://www.db-fiddle.com/f/hriFWqVy5RGbnsdj8i3aVG/1
For Inner join You should have to do like this way..
SELECT customer_id,phone_number FROM phone_numbers as pn,call_history as ch where pn.customer_id = ch.customer_id and call_start_time >= DATE_SUB(SYSDATE(), INTERVAL 30 DAY) GROUP BY phone_number;
Just add table name whatever you want to join and add condition
I have a query which is taking a long time to execute.
Table descriptions. These tables are very large so will give relevant columns in description. All Columns are varchar.
Table 1 - General
PK - CLAIM_ID
No of Records - 2.63 Mill,
Table 2 - Enrol
No of Records - 2.5 Million
Cols - CLAIM_ID(PK),POLICY_ID,MEMBER_ID
Table 3 - Member
No fo Records - 28 million
Cols - MEMBER_ID(PK),POLICY_GROUP_ID
Table 4 - Policy
No fo Records - 2 Million
Cols- POLICY_ID,policy_sub_general_type_id
table 5 - Balance
No of Records - 12 Million.
Columns
Query is
SELECT cg.CLAIM_ID,mem.Policy_group_ID ,
CAST(CASE when pol.policy_sub_general_type_id = 'PFL'
then (bal2.sum_insured - bal2.utilised_sum_insured)
when pol.policy_sub_general_type_id = 'PNF'
then (bal1.sum_insured - bal1.utilised_sum_insured)
end AS DECIMAL(10, 2) ) Balance_SI
FROM General cg
LEFT JOIN Enrol ce ON cg.CLAIM_ID = ce.CLAIM_ID
LEFT JOIN Member mem ON ce.MEMBER_ID = mem.MEMBER_ID
LEFT JOIN Policy pol ON pol.POLICY_ID = ce.POLICY_ID
LEFT join Balance bal1 ON bal1.MEMBER_ID = ce.MEMBER_ID
and bal1.MEMBER_ID is not null
LEFT join Balance bal2 ON bal2.Policy_group_ID = mem.Policy_group_ID
and bal2.Policy_group_ID is not null
GROUP BY cg.CLAIM_ID
Explain Statement shows
Select Type|table|Type|key|rows|Extra
_____________________________________
SIMPLE|cg |index|PRIMARY|2662233|Using Index
SIMPLE|ce |ref|index1|1|NULL
SIMPLE|mem|eq_ref|PRIMARY|1|using where
SIMPLE|pol|eq_ref|PRIMARY|1| Using Where
SIMPLE|bal1|ref|index2|3|Using Where
SIMPLE|bal2|ref|index1|1|using where
Server params
InnoDB_Buffer_pool - 10GB
InnoDB_Log_File_Size - 3GB
4 Core processor
All tables and columns have same collation and character set, So this is not a collation issue. Also also join columns are varchar. Explain statement shows ( I assume) tables are indexed well.
Query takes around 15 minutes to return first 50000 rows which is unacceptable at this point of time.For entire table it is still running for last 3 hours without any result.
No Idea why this is happening. Please help.
For starters, you can completely remove your "cg" alias General table unless you are using for some other columns you are not showing here. Reason, you have the claim ID directly from your enrollment table. Just removes extra level.
Next, your Group by is only on the claim, but the policy group ID is part of your select. Did you intend to have it aggregated per policy too? Can one claim be covered by multiple policy groups? If not and you are just trying to carry that forward, you can keep it via
MAX( mem.Policy_Group_ID ) as Policy_Group_ID
As noted by Strawberry, doing aggregates / group by where you MIGHT have Cartesian results could give you false answers.
I would also suggest editing your post and confirming some additional details such as the Balance Table. You have one total based on "PFL" for "PNF" we know there specific meaning behind them, but mean nothing to us. Your case/when is pulling the value from the "Bal1" vs "Bal2" alias why. Is this a condition where a specific policy group is NOT entered into the balance table and it falls into either some "generic bucket", or a bucket specific to a single policy? Such as regular coverage of "X", but you have a limit on category "Y"?
Below is cleaner SQL readability with removal of the general table.
SELECT
ce.CLAIM_ID,
mem.Policy_group_ID,
CAST(CASE when pol.policy_sub_general_type_id = 'PFL'
then (bal2.sum_insured - bal2.utilised_sum_insured)
when pol.policy_sub_general_type_id = 'PNF'
then (bal1.sum_insured - bal1.utilised_sum_insured) end AS DECIMAL(10,2)) Balance_SI
FROM
Enrol ce
LEFT JOIN Member mem
on ce.MEMBER_ID = mem.MEMBER_ID
LEFT join Balance bal2
on mem.Policy_group_ID = bal2.Policy_group_ID
and bal2.Policy_group_ID <> ''
LEFT JOIN Policy pol
on ce.POLICY_ID = pol.POLICY_ID
LEFT join Balance bal1
on ce.MEMBER_ID = bal1.MEMBER_ID
and bal1.MEMBER_ID <> ''
GROUP BY
ce.CLAIM_ID
Finally, looking at your case/when and join on Bal2 alias, you have no reference to a member ID, so lets show you the Cartesian Killer you are probably encountering. Example, Federal employees fall into a policy group and have 20k employees. Now you have one Enrollment record left-joining to the balance table? is it one record per policy group or one per member / policy group. If the per member / policy, you are plowing through 20k balance records every time trying to get value from Bal2. While the Balance table "Bal1" alias is explicit per member ID. So I know both fields are in the table and that might be killing you.
Again, please edit your existing post for clarification of details and relationships, especially 1:1 vs 1:n
This is not an answer yet
Your DB schema is not clear to me.
I have many questions and a lot of ideas how to speed this query up.
Lets take a look on your 1st part of query:
SELECT cg.CLAIM_ID,
mem.Policy_group_ID,
CAST(
CASE
when
pol.policy_sub_general_type_id = 'PFL' then
(bal2.sum_insured - bal2.utilised_sum_insured)
when pol.policy_sub_general_type_id = 'PNF' then
(bal1.sum_insured - bal1.utilised_sum_insured)
END
AS DECIMAL(10,2)
) Balance_SI
You have "inline" function calls, which hit the performance: CAST, CASE, bal1.sum_insured - bal1.utilised_sum_insured, bal2.sum_insured - bal2.utilised_sum_insured
If your app or whatever you do can accept not "formatted" result to be returned by query, I would suggest to remove CAST - it will speed up query a bit with no affect on real values returned. You can round those values later on application level.
Next is CASE, again if you have your app level (I hope) you can return raw data instead of transformed result. I mean you can return 3 columns: pol.policy_sub_general_type_id, bal1.sum_insured - bal1.utilised_sum_insured, bal2.sum_insured - bal2.utilised_sum_insured instead of CASE. But I suspect you don't need even this optimization. I'll show that later.
I have many questions about your JOINs as well. But since you did not reply on DRapp answer yet I will keep my questions for a while.
Lets go directly to the query I suspect will return almost the same data you need and discuss details later if you will have any particular questions.
SELECT
cg.CLAIM_ID,
mem.Policy_group_ID ,
SUM(bal.sum_insured - bal.utilised_sum_insured) Balance_SI
FROM `General` cg
LEFT JOIN Enrol ce
ON cg.CLAIM_ID = ce.CLAIM_ID
LEFT JOIN Member mem
ON ce.MEMBER_ID = mem.MEMBER_ID
LEFT JOIN Policy pol
ON pol.POLICY_ID = ce.POLICY_ID
AND (pol.policy_sub_general_type_id = 'PNF'
OR pol.policy_sub_general_type_id = 'PFL')
LEFT JOIN Balance bal
ON (bal.MEMBER_ID = ce.MEMBER_ID
AND bal.MEMBER_ID <> '')
OR (bal.Policy_group_ID = mem.Policy_group_ID
AND bal.Policy_group_ID <> '')
GROUP BY cg.CLAIM_ID, mem.Policy_group_ID
I am trying to make a table that includes join between 3 tables in the MSSS 2008. There is a fact table, a date table, and a course table. I should join them to make a base table. In date table there is a one parameter that name is Academic Year lookup, and the values in this parameter is like 2000/1, 2001/2. This parameter in the base table should separate to three parameter such as CensusYear, StartYear, and ApplicationYear. Therefore, I need the data table multiple times. I executed a inner join query, and already I have four inner join statement, but I am getting some extra years, and I'm losing some years. I believe, my query should be wrong somewhere.
The attached file is include the design view that created in the MS Access, it'll help to see the tables, and understand what I need to create.
[Design View in Ms Access][1]
SELECT
A.[EventCount],
B.[AcademicYearLookup] AS [CensusYear],
C.[AcademicYearLookup] AS [StartYear],
D.[AcademicYearLookup] AS [ApplicationYear],
B.[CurrentWeekComparisonFlag],
B.[AcademicWeekOfYear],
case
when A.[ApplicationCensusSK] = 1 then 'Same Year'
when A.[ApplicationCensusSK] = 2 then 'Next Year'
when A.[ApplicationCensusSK] = 5 then 'Last Year'
ELSE 'Other'
END as [CensusYearDescription],
B.[CurrentAcademicYear],
A.[StudentCodeBK],
A.[ApplicationSequenceNoBK],
A.[CourseSK],
A.[CourseGroupSK],
A.[CourseMoaSK],
A.[CboSK],
A.[CourseTaughtAbroadSK],
A.[ApplicationStatusSK],
A.[ApplicationFeeStatusSK],
A.[DecisionResponseSK],
A.[NationalityCountrySK],
A.[DomicileCountrySK],
A.[TargetRegionSK],
A.[InternationalSponsorSK] INTO dbo.[BaseTable3yrs]
FROM Student.FactApplicationSnapshot A
INNER JOIN Conformed.DimDate AS B ON A.[CensusDateSK] = B.[DateSK]
INNER JOIN Conformed.DimDate AS C ON A.[AcademicYearStartDateSK] = C.[DateSK]
INNER JOIN Conformed.DimDate AS D ON A.[ApplicationDateSK] = D.[DateSK]
INNER JOIN Student.DimCourse ON A.CourseSK = Student.DimCourse.CourseSK
WHERE (((B.CurrentAcademicYear) In (0,-1))
AND ((A.ApplicationCensusSK) In (1,2,5))
AND ((Student.DimCourse.DepartmentShortName)= 'TEACH ED'));
/* the query to check that the result it's correct or not, and I check it by academic week of year, and I found that I am lossing some data, and I have some extra data, means maybe join is wrong*/
select * from [BaseTable3yrs]
where [StudentCodeBK]= '26002423'
AND [ApplicationSequenceNoBK] = '0101'
order by [AcademicWeekOfYear]
When doing recursive joins like this, it's easy to get duplicate records. You could try gathering the Conformed data separately into a table variable and then joining to it. This would also make your query more readable.
You might also try a SELECT DISTINCT on your main query.
I'm trying to write a query which does the below:
For every guest who has the word “Edinburgh” in their address show the total number of nights booked. Be sure to include 0 for those guests who have never had a booking. Show last name, first name, address and number of nights. Order by last name then first name.
I am having problems with making the join work properly,
ER Diagram Snippet:
Here is my current (broken) solution:
SELECT last_name, first_name, address, nights
FROM booking
RIGHT JOIN guest ON (booking.booking_id = guest.id)
WHERE address LIKE '%Edinburgh%';
Here is the results from that query:
The query is partially complete, hoping someone can help me out and create a working version. I'm currently in the process of learning SQL so apologies if its a rather basic or dumb question!
Your query seems almost correct. You were joining the booking id with guets id which gave you some results because of overlapping (matching) ids, but this most likely doesn't correspond to the foreign keys. You should join on guest_id from booking to id from guest.
I'd add grouping to sum all booked nights for a particular guest (assuming that nights is an integer):
SELECT g.last_name, g.first_name, g.address, SUM(b.nights) AS nights
FROM guest AS g
LEFT JOIN booking AS b ON b.guest_id = g.id
WHERE g.address LIKE '%Edinburgh%'
GROUP BY g.last_name, g.first_name, g.address;
Are you sure that nights spent should be calculated using nights field? Why can it be null? If you'd like to show zero for null values just wrap it up with a coalesce function like that:
COALESCE(SUM(b.nights), 0)
Notes:
Rewriten RIGHT JOIN into LEFT JOIN, but that doesn't affect results - it's just cleaner for me
Using aliases eg. AS g makes the code shorter when specifying joining columns
Reference every column with their table alias to avoid ambiguity
SELECT g.first_name,
g.last_name,
g.address,
COALESCE(Sum(b.nights), 0)
FROM booking b
RIGHT JOIN guest g
ON ( b.guest_id = g.id )
WHERE address LIKE 'edinburgh%'
GROUP BY g.last_name,
g.first_name,
g.address;
This post answers your questions about how to make the query.
MySQL SUM with same ID
You can simply use COALESCE as referenced here to avoid the NULL Values
How do I get SUM function in MySQL to return '0' if no values are found?
Ok - I'm rewording my question in hopes of getting as response. I (with help from a co-worker) have created the following SQL query that pulls the EXACT results that I need to appear in an SSRS chart:
select
(SELECT pfsp.SavingsGoal
FROM Projects AS p INNER JOIN
Projects_PerformanceServicesProject AS pfsp ON p.Id = pfsp.Id INNER JOIN
ProjectSavingsGoalTypes AS gt ON pfsp.ProjectSavingsGoalType_Id = gt.Id
WHERE (p.Id = #Project_ID)) as SavingsGoal,
(SELECT
Sum(identifiedSum)
FROM #Yaks where UPPER(name) = 'DECLINED'
GROUP BY name)as IdentifiedDeclined,
(SELECT
Sum(identifiedSum)
FROM #Yaks) as identifiedTotal,
(SELECT
Sum(implementableSum)
FROM #Yaks where upper(name) = 'APPROVED'
GROUP BY name) as implementableSavingsApproved,
(SELECT
Sum(implementedSum)
FROM #Yaks
) as implementedSavingsTotal
What the chart should ultimately look like (generally speaking):
http://i1365.photobucket.com/albums/r745/twarden11/chart_mockup_zps22cfdbf3.png
Telling you everything I've tried would take all my characters, and would be good for a laugh, and that's about it. It was also be futile, as I am an extreme novice (this is my first time to build a chart - ever, please be clear and speak in non-technical terms when possible), and my efforts I can assure had nothing to do with what I need to be trying.
So what I need are plain instructions on how to turn this query into the table graphic that I've included. I can't express how desperate I am at this point. My co-worker said it would be easier to simply pull the exact data that I need in the query, but never told me how to convert the query to a chart.
Thanks so much.
I would redesign the SQL query to return 2 columns and 5 rows. The 1st column would describe the category e.g. Goal, Identified etc. The 2nd column would present the $ values.
This would probably require a series of SELECT ... UNION ALL ... clauses, one for each of the 5 rows required.
Then I would add the 1st column to the chart as the Category Group, and the 2nd column as the Values (series).