A faster SQL Query to get this done - sql-server-2008

I have a SP and UDF that brings me Totals. But I know there is a COUNT CASE WHEN query possible to get totals in a single go. Can anyone help me in this regard? My current query is just a redundant pause.
ALTER PROCEDURE [dbo].[GetMailBasketsForLocums]
AS
BEGIN
SET NOCOUNT ON;
SET FMTONLY OFF;
SET DATEFORMAT DMY;
SELECT DISTINCT Locum.OID,
Locum.FirstName + ' ' + Locum.LastName AS Name,
dbo.GetMailBasketTotalsForLocums(MailBasket.LocumId, 1) AS BookingsConfirmed,
dbo.GetMailBasketTotalsForLocums(MailBasket.LocumId, 3) AS BookingsCancelled,
dbo.GetMailBasketTotalsForLocums(MailBasket.LocumId, 5) AS BookingsSwitched,
(dbo.GetMailBasketTotalsForLocums(MailBasket.LocumId, 1) +
dbo.GetMailBasketTotalsForLocums(MailBasket.LocumId, 3) +
dbo.GetMailBasketTotalsForLocums(MailBasket.LocumId, 5)) AS Total
FROM MailBasket INNER JOIN
Locum
ON Locum.OID = MailBasket.LocumID
WHERE MailBasket.IsSent = 0
AND MailBasket.MailTypeID IN (1, 3, 5);
END
And
ALTER FUNCTION [dbo].[GetMailBasketTotalsForLocums]
(
#LocumID BIGINT,
#MailTypeID INT
)
RETURNS int
AS
BEGIN
DECLARE #Result int
SELECT #Result = COUNT(MailBasket.OID)
FROM MailBasket
WHERE MailBasket.MailTypeID = #MailTypeID
AND MailBasket.LocumID = #LocumID
AND MailBasket.IsSent = 0;
RETURN #Result
END
Regards.

SELECT
DISTINCT Locum.OID,
Locum.FirstName + ' ' + Locum.LastName AS Name,
COUNT(CASE WHEN MailBasket.MailTypeID = 1 THEN 1 ELSE NULL END) AS BookingsConfirmed,
COUNT(CASE WHEN MailBasket.MailTypeID = 3 THEN 1 ELSE NULL END) AS BookingsCancelled,
COUNT(CASE WHEN MailBasket.MailTypeID = 5 THEN 1 ELSE NULL END) AS BookingsSwitched,
COUNT(*) AS Total
FROM
MailBasket INNER JOIN
Locum
ON Locum.OID = MailBasket.LocumID
WHERE MailBasket.IsSent = 0
AND MailBasket.MailTypeID IN (1, 3, 5);
COUNT ignores NULLs to the conditional count works
You can also use SUM
SUM(CASE WHEN MailBasket.MailTypeID = 5 THEN 1 ELSE 0 END)

Try this:
SELECT DISTINCT Locum.OID, Locum.FirstName + ' ' + Locum.LastName AS Name,
COUNT(CASE WHEN MailBasket.MailTypeID = 1 THEN MailBasket.MailTypeID END) as BookingsConfirmed,
COUNT(CASE WHEN MailBasket.MailTypeID = 3 THEN MailBasket.MailTypeID END) as BookingsCancelled,
COUNT(CASE WHEN MailBasket.MailTypeID = 5 THEN MailBasket.MailTypeID END) as BookingsSwitched,
COUNT(*) as TOTAL
FROM MailBasket INNER JOIN
Locum
ON Locum.OID = MailBasket.LocumID
WHERE MailBasket.IsSent = 0
AND MailBasket.MailTypeID IN (1, 3, 5);

You can use a case inside a sum and group on the Locum records:
select
Locum.OID,
Locum.FirstName + ' ' + Locum.LastName AS Name,
sum(case when MailBasket.MailTypeID = 1 then 1 else 0 end) as BookingsConfirmed,
sum(case when MailBasket.MailTypeID = 3 then 1 else 0 end) as BookingsCancelled,
sum(case when MailBasket.MailTypeId = 5 then 1 else 0 end) as BookingsSwitched,
count(*) as Total
from
MailBasket
inner join Locum on Locum.OID = MailBasket.LocumID
where
MailBasket.IsSent = 0
and MailBasket.MailTypeID IN (1, 3, 5)
group by
Locum.OID, Locum.FirstName, Locum.LastName

Related

Slow MySQL queries using SUM()

I have to run two queries in my code to get my tenants balance. However, these queries are too slow.
First query, I get all the tenants and it's unit name:
SELECT t.TenantID
FROM Tenants t
JOIN Units u
ON t.UnitID = u.UnitID
Where t.Prospect = 2
AND t.PropertyID = 8
ORDER
BY CONCAT(Left(Replace(UnitName,'-',''),2),
REPEAT('0', (10-CHAR_LENGTH(UnitName))),
Right(Replace(UnitName,'-',''),
CHAR_LENGTH(Replace(UnitName,'-',''))-2
) )
It returns 500 rows
Then I get the balances in 4 conditions. This query will be inside of first query loop:
Select
SUM(CASE WHEN TransactionTypeID = 1 AND ChargeTypeID != 6 THEN TransactionAmount ELSE 0 END) AS TotalDebit,
SUM(CASE WHEN TransactionTypeID = 1 AND ChargeTypeID = 6 THEN TransactionAmount ELSE 0 END) AS HousingDebit,
SUM(CASE WHEN TransactionTypeID = 2 AND ChargeTypeID != 6 THEN TransactionAmount ELSE 0 END) AS TotalCredit,
SUM(CASE WHEN TransactionTypeID = 2 AND ChargeTypeID = 6 THEN TransactionAmount ELSE 0 END) AS HousingCredit
From TenantTransactions
Where TenantID= FirstQuery.TenantID
Am I doing the queries wrong? It's taking like 1 minute to run.
Do this in a single query with GROUP BY.
Try something like this:
SELECT t.TenantID, TotalDebit, HousingDebit, TotalCredit, HousingCredit
FROM Tenants t
JOIN Units u ON t.UnitID = u.UnitID
LEFT JOIN (
Select
TenantID,
SUM(CASE WHEN TransactionTypeID = 1 AND ChargeTypeID != 6 THEN TransactionAmount ELSE 0 END) AS TotalDebit,
SUM(CASE WHEN TransactionTypeID = 1 AND ChargeTypeID = 6 THEN TransactionAmount ELSE 0 END) AS HousingDebit,
SUM(CASE WHEN TransactionTypeID = 2 AND ChargeTypeID != 6 THEN TransactionAmount ELSE 0 END) AS TotalCredit,
SUM(CASE WHEN TransactionTypeID = 2 AND ChargeTypeID = 6 THEN TransactionAmount ELSE 0 END) AS HousingCredit
From TenantTransactions
Group By TenantID
) sums ON sums.TenantID = t.TenantID
Where t.Prospect = 2
AND t.PropertyID = 8
ORDER
BY CONCAT(Left(Replace(UnitName,'-',''),2),REPEAT('0', (10-CHAR_LENGTH(UnitName))),Right(Replace(UnitName,'-',''),CHAR_LENGTH(Replace(UnitName,'-',''))-2))
The inner query may still run for a while but it will only run once.
Try a compound covering index on TenantTransactions containing these columns: (TenantID, TransactionTypeID, ChargeTypeID, TransactionAmount) to optimize the query with the SUMs in it.
Try a compound index on Tenants with the columns (PropertyID, Prospect) in it.
Here's another way to do it with a subquery. You know, the performance problem might not be database performance, but the back and forth between your database and application server. So that is where a single query will help.
SELECT t.TenantID,
(SELECT SUM(CASE WHEN TransactionTypeID = 1 AND ChargeTypeID != 6 THEN TransactionAmount ELSE 0 END) From TenantTransactions TT WHERE TT.TenantID=t.TenantID) AS TotalDebit,
(SELECT SUM(CASE WHEN TransactionTypeID = 1 AND ChargeTypeID = 6 THEN TransactionAmount ELSE 0 END) From TenantTransactions TT WHERE TT.TenantID=t.TenantID) AS HousingDebit,
(SELECT SUM(CASE WHEN TransactionTypeID = 2 AND ChargeTypeID != 6 THEN TransactionAmount ELSE 0 END) From TenantTransactions TT WHERE TT.TenantID=t.TenantID) AS TotalCredit,
(SELECT SUM(CASE WHEN TransactionTypeID = 2 AND ChargeTypeID = 6 THEN TransactionAmount ELSE 0 END) From TenantTransactions TT WHERE TT.TenantID=t.TenantID) AS HousingCredit
FROM Tenants t
JOIN Units u
ON t.UnitID = u.UnitID
Where t.Prospect = 2
AND t.PropertyID = 8
ORDER
BY CONCAT(Left(Replace(UnitName,'-',''),2),REPEAT('0', (10-CHAR_LENGTH(UnitName))),Right(Replace(UnitName,'-',''),CHAR_LENGTH(Replace(UnitName,'-',''))-2))

mysql sum case not working

I am working on a mysql query using sum case. The query works for some of the cases but not for one case.
select orders.orderid, campers.description, dealers.name, orders.act_delivery,
sum(case orderitems.std
when 0 then
(case when orderitems.price = 0 then retail_price.price * orderitems. qty else orderitems.price * orderitems.qty end)
when 1 then
(case when orderitems.origqty = 0 then
0 else
(case when orderitems.price = 0
then retail_price.price * (orderitems.qty - orderitems.origqty)
else orderitems.price * (orderitems.qty - orderitems.origqty)
end)
end)
when 2 then
0
else
(case when orderitems.deletedprice = 0 then retail_price.price * orderitems. qty else orderitems.deletedprice * orderitems.qty end)
end) as retail
from orderitems, orders, dealers, campers, products, retail_price
where orderitems.orderid = orders.orderid
and orderitems.productid = products.prodid
and orderitems.productid =retail_price.prodid
and orders.dealerid = retail_price.dealer
and orders.dealerid = dealers.dealerid
and orders.camper = campers.camperid
and act_delivery > 0
and orderitems.std in (0, 1, 2)
and orders.dealerid not in (1, 9, 10, 12,15,16)
and spares_od = 0
and orders.act_delivery > '2016-10-01'
and orders.act_delivery <= '2016-10-31'
group by orders.orderid,orders.dealerid, orders.act_delivery order by orders.act_delivery, orders.orderid
The problem occurs when orderitems.std = 3. In either case I cannot get a result.
All other cases appear to be working correctly.

Mysql query optimization takes more than 30 sec

Hi friends can any one please help me to optimize this query ,it takes more than 30 sec.
any suggestion is warmly welcome.
Query :
SELECT
CONCAT(CCD.CONTACT_FIRST_NAME, ' ', CCD.CONTACT_LAST_NAME) AS NAME,
A.EMAIL_IDS,
CCD.UNSUBSCRIBE,
IFNULL(CSL.LOG_DATE, '') AS LOG_DATE,
CSL.IP_ADDRESS,
IF(CSL.BROWSER IS NULL, '', CSL.BROWSER) AS BROWSER,
(CASE
WHEN (UNSUBSCRIBE = 0 OR UNSUBSCRIBE IS NULL) THEN 'Opted In'
ELSE (CASE
WHEN (UNSUBSCRIBE = 1) THEN 'Opted Out'
ELSE (CASE
WHEN
((UNSUBSCRIBE = 2 OR UNSUBSCRIBE = 3)
AND CCD.CONTACT_ID NOT IN (SELECT
CONTACT_ID
FROM
CM_SUBSCRIPTION_MAIL_DATA
WHERE
IS_MAIL_SENT = 'Y'))
THEN
'Opt-In Request not Sent'
ELSE 'Opt-In Pending'
END)
END)
END) AS CURR_SUB,
(CASE
WHEN (SUBSCRIBE_FROM = 0) THEN 'Opted In'
ELSE (CASE
WHEN (SUBSCRIBE_FROM = 1) THEN 'Opted Out'
ELSE (CASE
WHEN (SUBSCRIBE_FROM = 2 OR SUBSCRIBE_FROM = 3) THEN 'Opt-In Pending'
ELSE ''
END)
END)
END) AS SUB_FROM,
SUBSCRIBE_FROM,
(CASE
WHEN (SUBSCRIBE_TO = 0) THEN 'Opted In'
ELSE (CASE
WHEN (SUBSCRIBE_TO = 1) THEN 'Opted Out'
ELSE (CASE
WHEN (SUBSCRIBE_TO = 2 OR SUBSCRIBE_TO = 3) THEN 'Opt-In Pending'
ELSE ''
END)
END)
END) AS SUB_TO,
SUBSCRIBE_TO
FROM
CM_CONTACT_DETAILS CCD
LEFT JOIN
ADDRESS A ON CCD.CONTACT_ID = A.FOREIGN_ID
LEFT JOIN
CM_SUBSCRIPTION_LOGS CSL ON CSL.CONTACT_ID = CCD.CONTACT_ID
WHERE
1 = 1
GROUP BY CSL.LOG_DATE , CCD.CONTACT_ID
ORDER BY NAME DESC , LOG_DATE DESC
LIMIT 0 , 20
Your case/when can be simplified, but I believe the killer of your query was the correlated NOT IN SELECT clause for your Opt-in request not sent.
I've change your query to do a left-join to your mailing table based on the contact AND the mailed status of "Y". So this simplifies your case to only have to check for IS NULL of the mailing table contact ID.
You can't do too much on performance since your Group by is by columns from different tables that won't take advantage of any index optimization, then all that ordered by the name makes it more of an issue... but the correlated subquery per the field being executed every time to a left-join SHOULD help.
SELECT
CONCAT(CCD.CONTACT_FIRST_NAME, ' ', CCD.CONTACT_LAST_NAME) AS NAME,
A.EMAIL_IDS,
CCD.UNSUBSCRIBE,
IFNULL(CSL.LOG_DATE, '') AS LOG_DATE,
CSL.IP_ADDRESS,
IF(CSL.BROWSER IS NULL, '', CSL.BROWSER) AS BROWSER,
CASE WHEN UNSUBSCRIBE = 0 OR UNSUBSCRIBE IS NULL THEN 'Opted In'
WHEN UNSUBSCRIBE = 1 THEN 'Opted Out'
WHEN UNSUBSCRIBE IN (2,3) AND SMD.CONTACT_ID IS NULL THEN 'Opt-In Request not Sent'
ELSE 'Opt-In Pending' END AS CURR_SUB,
CASE WHEN SUBSCRIBE_FROM = 0 THEN 'Opted In'
WHEN SUBSCRIBE_FROM = 1 THEN 'Opted Out'
WHEN SUBSCRIBE_FROM = 2 OR SUBSCRIBE_FROM = 3 THEN 'Opt-In Pending'
ELSE '' END AS SUB_FROM,
SUBSCRIBE_FROM,
CASE WHEN SUBSCRIBE_TO = 0 THEN 'Opted In'
WHEN SUBSCRIBE_TO = 1 THEN 'Opted Out'
WHEN SUBSCRIBE_TO = 2 OR SUBSCRIBE_TO = 3 THEN 'Opt-In Pending'
ELSE '' END AS SUB_TO,
SUBSCRIBE_TO
FROM
CM_CONTACT_DETAILS CCD
LEFT JOIN ADDRESS A
ON CCD.CONTACT_ID = A.FOREIGN_ID
LEFT JOIN CM_SUBSCRIPTION_LOGS CSL
ON CSL.CONTACT_ID = CCD.CONTACT_ID
LEFT JOIN CM_SUBSCRIPTION_MAIL_DATA SMD
ON CCD.CONTACT_ID = SMD.CONTACT_ID
AND IS_MAIL_SENT = 'Y'
WHERE
1 = 1
GROUP BY
CSL.LOG_DATE,
CCD.CONTACT_ID
ORDER BY
NAME DESC,
LOG_DATE DESC
LIMIT
0, 20

SUM for more fields in mysql

I have a query,which give me wrong result.Here is my query.
SELECT sum(open1) open1, sum(closed1) closed,sum(pending1) NotSpecified,
status,type,sub_status,created_date,bystatus,lst_type
FROM (
SELECT agent_1_id,status,type,sub_status,created_date,'Open' as bystatus,
(CASE WHEN type = 1 THEN 'Rent'
WHEN type = 2 THEN 'Sale'
ELSE 'Not Specified' END) as lst_type,
count(*) as open1, 0 closed1, 0 pending1
FROM crm_mydeals
where status = 1 AND agent_1_id>0 and is_active=1
GROUP BY status
UNION ALL
SELECT agent_1_id,status,type,sub_status,created_date,'Closed' as bystatus,
(CASE WHEN type = 1 THEN 'Rent'
WHEN type = 2 THEN 'Sale'
ELSE 'Not Specified' END) as lst_type,
0 open1, count(*) as closed1, 0 pending1
FROM crm_mydeals
where status = 2 AND agent_1_id>0 and is_active=1
GROUP BY status
UNION ALL
SELECT agent_1_id,status,type,sub_status,created_date,'NotSpecified' as bystatus,
(CASE WHEN type = 1 THEN 'Rent'
WHEN type = 2 THEN 'Sale'
ELSE 'Not Specified' END) as lst_type,
0 open1, 0 closed1, count(*) as pending1
FROM crm_mydeals
where status = 3 AND agent_1_id>0 and is_active=1
GROUP BY status
) s
WHERE DATE(created_date) BETWEEN '2013-11-22' AND '2014-2-22'
GROUP BY status
If you simplify the query, it might help to find the correct answer?
SELECT SUM(CASE WHEN status = 1 THEN 1 ELSE 0) AS open1,
SUM(CASE WHEN status = 2 THEN 1 ELSE 0) AS closed,
SUM(CASE WHEN Status = 3 THEN 1 ELSE 0) AS NotSpecified,
status,
type,
sub_status,
created_date,
(CASE WHEN status = 1 THEN 'Open'
WHEN status = 2 THEN 'Closed'
WHEN status = 3 THEN 'NotSpecified') AS bystatus,
(CASE WHEN type = 1 THEN 'Rent'
WHEN type = 2 THEN 'Sale'
ELSE 'Not Specified' END) as lst_type
FROM crm_mydeals
WHERE status IN (1,2,3)
AND agent_1_id > 0
AND is_active=1
AND DATE(created_date) BETWEEN '2013-11-22' AND '2014-2-22'
GROUP BY status, type, sub_status, created_date

mySQL query comparing rows and columns

I have a database of user answers from a quiz which contains 8 questions. Q1 - Q8 all are their own columns, I'd like to compare all the rows and get a number back for everyone who answered the same for at least 5 questions.
so here, rows 5 and 6 would count as 2. Basically I'm trying to get a number for everyone who answered at least 5 questions the same. Is this possible with a mySQL query?
EDIT:
Here the user enters D B D A B C D B, matching with 2 similarly answered quizzes. The query here would return a count of 2.
If we test using just your single line of D B D A B C D B we can use the following example:
SELECT * FROM `answers` WHERE ((CASE WHEN q1 = 'D' THEN 1 ELSE 0 END) +
(CASE WHEN q2 = 'B' THEN 1 ELSE 0 END) +
(CASE WHEN q3 = 'D' THEN 1 ELSE 0 END) +
(CASE WHEN q4 = 'A' THEN 1 ELSE 0 END) +
(CASE WHEN q5 = 'B' THEN 1 ELSE 0 END) +
(CASE WHEN q6 = 'C' THEN 1 ELSE 0 END) +
(CASE WHEN q7 = 'D' THEN 1 ELSE 0 END) +
(CASE WHEN q8 = 'B' THEN 1 ELSE 0 END)) >= 5;
However, if we then want to go a step further and test each answer against the other answers in the table we can use the following statement:
SELECT *, (SELECT COUNT(answer_sub.idanswers) FROM `answers` answer_sub
WHERE ((CASE WHEN answer_sub.q1 = a.q1 THEN 1 ELSE 0 END) +
(CASE WHEN answer_sub.q2 = a.q2 THEN 1 ELSE 0 END) +
(CASE WHEN answer_sub.q3 = a.q3 THEN 1 ELSE 0 END) +
(CASE WHEN answer_sub.q4 = a.q4 THEN 1 ELSE 0 END) +
(CASE WHEN answer_sub.q5 = a.q5 THEN 1 ELSE 0 END) +
(CASE WHEN answer_sub.q6 = a.q6 THEN 1 ELSE 0 END) +
(CASE WHEN answer_sub.q7 = a.q7 THEN 1 ELSE 0 END) +
(CASE WHEN answer_sub.q8 = a.q8 THEN 1 ELSE 0 END)) >= 5
AND answer_sub.idanswers <> a.idanswers) as matching
FROM `answers` a
WHERE (SELECT COUNT(answer_sub.idanswers) FROM `answers` answer_sub
WHERE ((CASE WHEN answer_sub.q1 = a.q1 THEN 1 ELSE 0 END) +
(CASE WHEN answer_sub.q2 = a.q2 THEN 1 ELSE 0 END) +
(CASE WHEN answer_sub.q3 = a.q3 THEN 1 ELSE 0 END) +
(CASE WHEN answer_sub.q4 = a.q4 THEN 1 ELSE 0 END) +
(CASE WHEN answer_sub.q5 = a.q5 THEN 1 ELSE 0 END) +
(CASE WHEN answer_sub.q6 = a.q6 THEN 1 ELSE 0 END) +
(CASE WHEN answer_sub.q7 = a.q7 THEN 1 ELSE 0 END) +
(CASE WHEN answer_sub.q8 = a.q8 THEN 1 ELSE 0 END)) >= 5
AND answer_sub.idanswers <> a.idanswers) > 0
Because FALSE is 0 and TRUE is 1 in MySQL:
SELECT COUNT(*)
FROM quiz
WHERE ( (q1=#q1) + (q2=#q2) + (q3=#q3) + (q4=#q4)
+ (q5=#q5) + (q6=#q6) + (q7=#q7) + (q8=#q8)
) >= 5