EXCEPT query to get info year to date - sql-server-2008

I have a table which holds account transactions for each account, I’ve written a query that tells me when an account has paid one month, but not the next based on the Parameter MonthID (from cal lookup table)
DECLARE #MonthID INT
SET #MonthID = 128
SELECT AccountNo, Emp,
FROM Table1 AS t INNER JOIN
tblcal AS cl ON t.date = cl.Date
WHERE cl.MonthID = #MonthID
EXCEPT
SELECT AccountNo, Emp,
FROM Table1 AS t INNER JOIN
tblcal AS cl ON t.date = cl.Date
WHERE cl.MonthID = #MonthID+1
Query works great for getting the records for the specified month however I need to look at getting this for every month of the year and have no idea how to factor that requirement in?
Is a cursor the best way for doing this? I’ve read a lot of negative’s about cursors?
At the moment it returns data like:
MonthID | SkippedPay
128 | 12445
(as i perform an count on the account numbers)
What i need is to get it for the last 12 months, so the same as above but with 12 months of data, this is what is making me think of a cursor to go through each month and populate a table?
MonthID | SkippedPay
128 | 12445
129 | 1256

What about the following approach:
You left join the dataset of one month against the data for the following month and return the rows in which there is no corresponding data in the following month.
DECLARE #MonthID INT
SET #MonthID = 128
SELECT MonthA.AccountNo, MonthA.Emp FROM
(SELECT AccountNo, Emp, cl.MonthID as [Month]
FROM Table1 AS t INNER JOIN
tblcal AS cl ON t.date = cl.Date
WHERE cl.MonthID >= #MonthID-12 and cl.MonthID <= #MonthID) AS MonthA
LEFT JOIN
(SELECT AccountNo, Emp, cl.MonthID as [Month]
FROM Table1 AS t INNER JOIN
tblcal AS cl ON t.date = cl.Date
WHERE cl.MonthID >= #MonthID-11 and and cl.MonthID <= #MonthID+1) AS MonthB on MonthA.AccountNo = MonthB.AccountNo AND MonthA.Emp = MonthB.Emp AND (MonthA.[Month]+1) = Monthb.[Month]
WHERE MonthB.AccountNo IS NULL
p.s.: You got a syntax error in your query: "Emp," with no following field.

Related

Retrieve all the values that are in the the row with the max value

I have a table that looks like this:
For each COMPANY there are multiple NATURAL_PERSON_ID, every NATURAL_PERSON have a date in which an audit was performed FECHA_DE_REPORTE and as a company there is a date in which the first loan was give to that company.
What I want is to select for each NATURAL_PERSON all the FOLIO_CONSULTA whose FECHA_DE_REPORTE is less or equal to FIRST_LOAN (the date in which the first loan was given for that company) Then I need to find the MAX date among each group and keep al the information (the whole row) for the value that fulfills all these conditions, and all this for each NATURAL_PERSON
So for this example the result I expected is all the information of the second row since this is the MAX() of FECHA_DE_REPORTE by COMPANY AND NATURAL_PERSON.
I have tried:
SELECT NPC.COMPANY_ID
,NPC.NATURAL_PERSON_ID
,NPS.DIGITAL_SIGNATURE_ID
,CDC.FOLIO_CONSULTA
,CDC.FECHA_DE_REPORTE
,FIRST_LOAN.FIRST_LOAN
,MAX(CDC.FECHA_DE_REPORTE) MAX_FOLIO_CONSUTA
FROM KONFIO.NATURAL_PERSON_COMPANY NPC
LEFT JOIN KONFIO.NATURAL_PERSON_SIGNATURE NPS ON NPS.NATURAL_PERSON_ID = NPC.NATURAL_PERSON_ID
JOIN KONFIO.CDC_RESPONSE CDC ON CDC.DIGITAL_SIGNATURE_ID= NPS.DIGITAL_SIGNATURE_ID
JOIN
(
SELECT CAPP.COMPANY_ID
,MIN(LOAN.DOCUMENTATION_DATE) FIRST_LOAN
FROM KONFIO.COMPANY_APPLICATION CAPP
JOIN KONFIO.LOAN ON LOAN.APPLICATION_ID = CAPP.APPLICATION_ID
GROUP BY CAPP.COMPANY_ID) FIRST_LOAN ON FIRST_LOAN.COMPANY_ID = NPC.COMPANY_ID
WHERE CDC.FECHA_DE_REPORTE <= FIRST_LOAN.FIRST_LOAN
AND NPC.COMPANY_ID IN (1033)
GROUP BY NPC.COMPANY_ID, NPC.NATURAL_PERSON_ID
but it retrieves the first value that finds so the FOLIO_CONSULTA does not correspond to the FOLIO_CONSULTA of the MAX() FECHA_DE_REPORTE
Any help would be appreciated
You should join the subquery for MAX(FECHA_DE_REPORTE) on table CDC_RESPONSE
SELECT NPC.COMPANY_ID
,NPC.NATURAL_PERSON_ID
,NPS.DIGITAL_SIGNATURE_ID
,CDC.FOLIO_CONSULTA
,CDC.FECHA_DE_REPORTE
,FIRST_LOAN.FIRST_LOAN
,T.MAX_FOLIO_CONSUTA
FROM KONFIO.NATURAL_PERSON_COMPANY NPC
INNER JOIN (
SELECT DIGITAL_SIGNATURE_ID
, MAX(FECHA_DE_REPORTE) MAX_FOLIO_CONSUTA
FROM KONFIO.CDC_RESPONSE
GROUP BY DIGITAL_SIGNATURE_ID
) T ON T.DIGITAL_SIGNATURE_ID = NPS.DIGITAL_SIGNATURE_ID
AND T.MAX_FOLIO_CONSUTA = CDC.FECHA_DE_REPORTE
LEFT JOIN KONFIO.NATURAL_PERSON_SIGNATURE NPS ON NPS.NATURAL_PERSON_ID = NPC.NATURAL_PERSON_ID
JOIN KONFIO.CDC_RESPONSE CDC ON CDC.DIGITAL_SIGNATURE_ID= NPS.DIGITAL_SIGNATURE_ID
JOIN
(
SELECT CAPP.COMPANY_ID
,MIN(LOAN.DOCUMENTATION_DATE) FIRST_LOAN
FROM KONFIO.COMPANY_APPLICATION CAPP
JOIN KONFIO.LOAN ON LOAN.APPLICATION_ID = CAPP.APPLICATION_ID
GROUP BY CAPP.COMPANY_ID) FIRST_LOAN ON FIRST_LOAN.COMPANY_ID = NPC.COMPANY_ID
WHERE CDC.FECHA_DE_REPORTE <= FIRST_LOAN.FIRST_LOAN
AND NPC.COMPANY_ID IN (1033)
GROUP BY NPC.COMPANY_ID, NPC.NATURAL_PERSON_ID
...... missing part

joint three tables in Mysql

I have three tables employee, promotion and punishment
Employee’s table structure something like this
Id int
Fullname varchar
...............
promotionDate date
Promotion’s table structure is like this
id int
emp_id int
directorateDate date
And punishment’s table structure is like this
id int
emp_id int
direcotorateDate date
Let’s say employee table has 200 records, each month a group of employees have promotion (after serving one year), I want to get the list of all employees in the current month that get promotion
I can easily get the list by this query
SELECT *
FROM employee
WHERE MONTH(promotionDate) = MONTH(CURRENT_DATE())
AND YEAR(promotionDate) = YEAR(CURRENT_DATE())
My question is
I want to count number of punishments and promotions each employee got in the current year from punishment and promotion table respectively
I did this query but it did not get right results
SELECT e.fullname , COUNT(punish.emp_id) as siza ,COUNT(pro.emp_id) as supas
FROM emp_employee as e
LEFT JOIN emp_punishment as punish on punish.emp_id=e.id
LEFT JOIN emp_promotion as pro on e.id=pro.emp_id
WHERE ((MONTH(e.promotionDate) = MONTH(CURRENT_DATE())
AND YEAR(e.promotionDate) = YEAR(CURRENT_DATE()))
AND ( YEAR(punish.directorate_date) = YEAR(CURRENT_DATE()) )
AND ( YEAR(pro.directorate_date) = YEAR(CURRENT_DATE()) )
GROUP BY e.fullname;
Any help please.
By joining directly the 3 tables you get duplicate rows.
Group by emp_id and aggregate separately each of the tables emp_punishment and emp_promotion and join the results to the table emp_employee.
select e.fullname, coalesce(pu.siza, 0) siza, coalesce(pr.supas, 0) supas
from emp_employee as e
left join (
select emp_id, count(*) siza
from emp_punishment
where year(directorate_date) = year(CURRENT_DATE)
group by emp_id
) pu on pu.emp_id = e.id
left join (
select emp_id, count(*) supas
from emp_promotion
where year(directorate_date) = year(CURRENT_DATE)
group by emp_id
) pr on pr.emp_id = e.id
I used only the condition:
where year(directorate_date) = year(CURRENT_DATE())
because in your question you say:
I want to count number of punishments and promotions each employee got in the current year from punishment and promotion
Removing MONTH() function, and moving each condition to their respective place, instead of within the WHERE clause should resolve the issue (Since, they're considered as if INNER JOINs with the current style ).
Only keep common column e.promotionDate within the WHERE clause :
SELECT e.fullname,
COUNT(punish.emp_id) as siza ,
COUNT(pro.emp_id) as supas
FROM emp_employee as e
LEFT JOIN emp_punishment as punish
ON punish.emp_id=e.id
AND YEAR(punish.directorate_date) = YEAR(CURRENT_DATE())
LEFT JOIN emp_promotion as pro
ON e.id=pro.emp_id
AND YEAR(pro.directorate_date) = YEAR(CURRENT_DATE()))
WHERE YEAR(e.promotionDate) = YEAR(CURRENT_DATE())
GROUP BY e.fullname;

Complex subqueries wihtin CASE statement

I have this query that calculates the expected value of a receipt, based on the 'channel' of acquisition of the user, on the 'id_store' and on the 'country' of the user.
The resulting query has this columns.
Count | id_store | country | price_usd | channel | T2S | ExpectedRevenue | ExpectedRevenue_projections
(note: ExpectedRevenue = count * price_usd * T2S)
In this query I want to know the value of those receipts that where purchased on August and expire on September (these products have a trial period, and we calculate their value is based on the historical conversion rate (Trial 2 subscription a.k.a. T2S)).
The problem arises when we do not have historical data for some scenarios; either we started selling different with id_stores, new country, channel, or any combination of the previous ones and the T2S becomes as a 'null' value and column 'ExpectedRevenue' therefore has 'null' values' ---> That is why I have a 'CASE' function that makes T2S to take the value of '.28' whenever this happens and I only have to sum the column 'ExpectedRevenue_projections' in order to get the final result.
However, this '.28' is just a fixed value that is not accurate at all (we have products from 10 to 120$...).
That's why I'm asking dear stackoverflow contributors is to replace this '28%' for another T2S that in this case would be calculated historically according to 'country' and 'price_usd' intervals such as (0-15), (15.01-29.99), (30 - 49.99)... and so on.
(The reason to put intervals is that 'price_usd' is calculated based on the exchange rate at the moment of the creation so the same product purchase by a customer in foreign currency could be 49.99, 49.98 , 49.97... and T2S would come as 'null' if there is not historical data).
Thank you
SELECT
r.count AS count,
r.id_store AS id_store,
r.country AS country,
r.price_usd AS price_usd,
r.channel AS channel,
a.count / (a.count + u.count) AS T2S,
r.count * r.price_usd * (a.count / (a.count + u.count)) AS ExpectedRevenue,
CASE WHEN (a.count / (a.count + u.count)) is null then (r.count * r.price_usd * .28) else r.count * r.price_usd * (a.count / (a.count + u.count)) END AS ExpectedRevenue_projections
FROM
(
select count(distinct(r.user_id)) AS count, p.id_store, u.country, r.price_usd, channel
from receipts as r
inner join products as p on p.id=r.product_id
inner join customers as c on r.customer_id=c.id
inner join users as u on u.id=r.user_id
where
MONTH(DATE_ADD(DATE_ADD(r.original_subscription_at, INTERVAL p.trial_days DAY), INTERVAL p.trial_months MONTH)) = 9
and YEAR(original_subscription_at) = 2017
and MONTH(original_subscription_at) = 8
and YEAR(r.expiration_at) = 2017
and MONTH(r.expiration_at) = 9
and r.status='trial'
and p.platform not in ('manual')
and c.tester=0
group by 2,3,4,5
) AS r
LEFT JOIN
(
select count(distinct r.user_id) AS count, u.country, p.id_store, channel
from receipts as r
inner join products as p on p.id=r.product_id
inner join users as u on u.id=r.user_id
where year(r.created_at)=2017
AND r.status = 'active'
group by 2,3,4
) AS a
ON r.country = a.country AND r.id_store = a.id_store AND r.channel = a.channel
LEFT JOIN
(
select count(distinct r.user_id) AS count, u.country, p.id_store, channel
from receipts as r
inner join products as p on p.id=r.product_id
inner join users as u on u.id=r.user_id
where year(r.created_at)=2017
AND r.status = 'unpaid'
group by 2,3,4
) AS u
ON r.country = u.country AND r.id_store = u.id_store AND r.channel = u.channel

What is the effect of performance of a SQL query when have aggregate function and where clause

I have 2 queries :
1.
SELECT u.unitId unitId,
u.unitScode 'unitScode',
( Cast(Count(vd.Date) AS FLOAT) / u.timeDiff ) * 100 'BookingCount',
u.tradeStartTime,
u.tradeStopTime,
u.minimumSlot,
u.maximumTerm
FROM #allDates vd
INNER JOIN CommcmlBookingDetail cd
ON vd.Date BETWEEN cd.dtFromTime AND cd.dtToTime
AND Datepart(minute, vd.date) = Datepart(minute, cd.dtFromTime)
INNER JOIN CommCmlBooking cb
ON cb.hMy = cd.hBooking
AND cb.iStatus = 1
AND cb.iType = 525
INNER JOIN #unitsInfo u
ON u.unitId = cb.hUnit
AND Cast(vd.Date AS DATE) BETWEEN Cast(#BookingFromDate AS DATE) AND Cast(#BookingToDate AS DATE)
AND Cast(vd.Date AS TIME) BETWEEN Cast(u.tradeStartTime AS TIME) AND Cast(u.tradeStopTime AS TIME)
WHERE cb.hRecord = case when #amendmentId = 0 then cb.hRecord else #amendmentId end
GROUP BY u.unitId,
u.unitScode,
u.minimumSlot,
u.tradeStartTime,
u.timeDiff,
u.tradeStopTime,
u.maximumTerm;
2.
INSERT INTO #tempBookingCount
SELECT u.unitId,
u.timeDiff
FROM #allDates vd
INNER JOIN CommcmlBookingDetail cd
ON vd.Date BETWEEN cd.dtFromTime AND cd.dtToTime
AND Datepart(minute, vd.date) = Datepart(minute, cd.dtFromTime)
INNER JOIN CommCmlBooking cb
ON cb.hMy = cd.hBooking
AND cb.iStatus = 1
AND cb.iType = 525
INNER JOIN #unitsInfo u
ON u.unitId = cb.hUnit
AND Cast(vd.Date AS DATE) BETWEEN Cast(#BookingFromDate AS DATE) AND Cast(#BookingToDate AS DATE)
AND Cast(vd.Date AS TIME) BETWEEN Cast(u.tradeStartTime AS TIME) AND Cast(u.tradeStopTime AS TIME)
WHERE cb.hRecord = case when #amendmentId = 0 then cb.hRecord else #amendmentId end
INSERT INTO #unitBookingCount
SELECT tt.unitID,
u.unitScode,
( Cast(Count(tt.unitID) AS FLOAT) / tt.timeDiff ) * 100,
u.tradeStartTime,
u.tradeStopTime,
u.minimumSlot,
u.maximumTerm
FROM #tempBookingCount tt
INNER JOIN #unitsInfo u
ON u.unitId = tt.unitID
GROUP BY tt.unitID,
tt.timeDiff,
u.tradeStartTime,
u.tradeStopTime,
u.minimumSlot,
u.maximumTerm,
u.unitScode
I have separated the first query into 2 parts, and i can a huge difference in the performance!
The first query take 14 seconds when executed for 5 months where as next query take 4 seconds.
Your original query uses 2 table variables #allDates and #unitsInfo.
By using table variables, you aren't giving SQL a fighting chance of optimizing the query because there are no statistics on table variables and the row count estimations and query plan are impacted.
One reference, you can find many more:
http://blogs.msdn.com/b/psssql/archive/2010/08/24/query-performance-and-table-variables.aspx
Try the original with #TempTables instead of #TableVars

MySQL Querying database for time periods not already assigned

I have a rather complex-seeming query that will form the basis for an online classroom scheduling tool. My challenge is to develop a method to identify which classes a user is signed up for in the st_schedule table, then deduce from the overall table of classes, st_classes, which other classes are available that don't conflict with the user's current classes.
For example, if a user has an entry in st_schedule assigning them to a class from 8:00am to 9:00am, they would be ineligible for any class whose time fell between 8:00am and 9:00am. A class that ran 7:15am - 8:15am would make the user ineligible. I store the start times and end times of classes in the database separately for comparison purposes. It's important that this be as flexible as possible, so the concept of "blocking" times and assigning times to blocks is not a possibility.
Here are excerpts from the tables:
table st_classes (holds class information)
id
start_time
end_time
table st_schedule (holds schedule information)
id
user_id
class_id
I certainly could do this in a series of loops server-side, but I have to think that there's a MySQL method that can do this type of operation in one fell swoop.
You want to join the two tables together to represent the user's classes, and then find unregistered classes where the start time and end time do not fall between the start and end time of the user's classes.
Something like this. Completely off the cuff and untested:
SELECT
*
FROM
st_schedule s
INNER JOIN st_classes c ON c.id = s.class_id
INNER JOIN st_classes all_classes
ON all_classes.start_time NOT BETWEEN c.start_time AND c.end_time
AND all_classes.end_time NOT BETWEEN c.start_time AND c.end_time
WHERE
s.user_id = 1
Edit: Try #2
I only have a moment to look at this. I think I reversed the second join clauses. The all_classes alias represents the full list of classes, where the "c" alias represents the classes that the student is signed up for.
SELECT DISTINCT
all_classes.*
FROM
st_schedule s
INNER JOIN st_classes c ON c.id = s.class_id
INNER JOIN st_classes all_classes
ON c.start_time NOT BETWEEN all_classes.start_time AND all_classes.end_time
AND c.end_time NOT BETWEEN all_classes.start_time AND all_classes.end_time
WHERE
s.user_id = 1
This is using table variables in mssql but the sql selects should translate over to mysql
First the sample data
DECLARE #st_classes TABLE
(
ID INT NOT NULL,
Title VARCHAR(40) NOT NULL,
StartTime DATETIME NOT NULL,
EndTime DATETIME NOT NULL
)
DECLARE #st_schedule TABLE
(
ID INT NOT NULL,
UserID INT NOT NULL,
ClassID INT NOT NULL
)
INSERT INTO #st_classes (ID, Title, StartTime, EndTime)
SELECT 1,'Class1','08:00:00','09:30:00' UNION
SELECT 2,'Class2','09:30:00','11:30:00' UNION
SELECT 3,'Class3','11:30:00','16:00:00' UNION
SELECT 4,'Class4','16:00:00','17:30:00' UNION
SELECT 5,'Class5','09:00:00','11:45:00' UNION
SELECT 6,'Class6','07:00:00','18:00:00'
INSERT INTO #st_schedule(ID, UserID, ClassID)
SELECT 1,1,1 UNION
SELECT 2,1,2 UNION
SELECT 3,2,6
Next a bit of sql to confirm the tables join OK (selecting scheduled courses for user with an ID of 1) - Returns class 1 and 2
SELECT *
FROM #st_schedule AS S INNER JOIN
#st_classes AS C ON S.ClassID = C.ID
WHERE S.UserID = 1
Now we need to select all the ID of the courses where they overlap time wise with the users scheduled ones (including the scheduled ones) - Returns 1,2,5,6
SELECT AC.ID
FROM #st_classes AS AC
INNER JOIN ( SELECT C.StartTime,
C.EndTime
FROM #st_schedule AS S
INNER JOIN #st_classes AS C ON S.ClassID = C.ID
WHERE S.UserID = 1
) AS UC ON ( AC.StartTime < DATEADD(ss, -1, UC.EndTime)
AND DATEADD(ss, -1, UC.EndTime) > UC.StartTime
)
GROUP BY AC.ID
Now we need to select all courses where the Course ID is not in our list of overlapping course IDs. - Returns course 3 and 4
SELECT *
FROM #st_classes
WHERE ID NOT IN (
SELECT AC.ID
FROM #st_classes AS AC
INNER JOIN ( SELECT C.StartTime,
C.EndTime
FROM #st_schedule AS S
INNER JOIN #st_classes AS C ON S.ClassID = C.ID
WHERE S.UserID = 1
) AS UC ON ( AC.StartTime < DATEADD(ss, -1, UC.EndTime)
AND DATEADD(ss, -1, UC.EndTime) > UC.StartTime
)
GROUP BY AC.ID )
Change the user ID filter to 2 and you should not get any returned as the course assigned to that user overlaps all courses.