COUNT(DISTINCT()) Return false value - sql-server

I try to count total employee in my subquery table. Suppose the count result will return 0, but it keeps returning 1.
If I try to return only employee_id and month together, I didn't get any return value for may which is correct, but each time I try to count(distinct), I will get 1 as my return value. This is my sql
SELECT
count (distinct(CASE WHEN (x.month =5 and x.employee_id <> 0) THEN
x.employee_id
ELSE 0 END)) as test_may
FROM(
(
SELECT
h.month,
h.employee_id,
eb.employee_no,
ee.company_code,
h.amount,
h.year,
h.trx_type,
h.trx_code,
v.trx_desc,
h.frequency,
h.run_sequence
FROM
v_employee h,
v_trans v,
employee_emp ee,
employee eb
WHERE
( h.year = 2014 ) AND
( h.employee_id = ee.employee_id ) AND
( ee.employee_id = eb.employee_id ) AND
( h.employee_no = eb.employee_no ) AND
( h.trx_code = v.trx_code ) AND
( h.trx_type = v.trx_type ) AND
( v.company_code = ee.company_code OR v.company_code is NULL) AND
( h.trx_type IN ('S','B','N','O','A','D','L') )
)
)x,
employee_emp ee,
employee eb
WHERE
( x.employee_id = ee.employee_id ) AND
( ee.employee_id = eb.employee_id ) AND
( x.employee_no = eb.employee_no ) AND
( x.year = 2014 )

The count as you have it now will also count the 0 that is in the ELSE clause of the CASE expression. Even with DISTINCT still one instance of that 0 will be counted.
Remove the ELSE 0 so that you have NULL -- which is not counted:
count (distinct(CASE WHEN x.month =5 and x.employee_id <> 0
THEN x.employee_id
END)) as test_may
Note that with NULLIF you can shorten this expression to:
count (distinct(CASE x.month WHEN 5 THEN NULLIF(x.employee_id, 0) END)) as test_may

Your count will return the same count even in both cases. Because you are giving the value for Count function in both the cases.
Change from
count (distinct(CASE WHEN (x.month =5 and x.employee_id <> 0) THEN
x.employee_id
ELSE 0 END))
To
count (distinct(CASE WHEN (x.month =5 and x.employee_id <> 0) THEN
x.employee_id
ELSE NULL END))
Count will just count the values whether it is 0 or 100 as 1 & skip null values while counting. So in the Else condition NULL will give you correct output.

Related

Bad bind variables in oracle stored procedure

I am totally new to stored procedures. I have a query and I wanna use it for a web service. I am going to use Nhibernate. I have tried couple of procedures and worked well. But this one giving me problems. I tried to compile the stored procedure but giving me errors.
create or replace
PROCEDURE GET_RSM_LIFE_AGENT_DUES (p_recordset OUT SYS_REFCURSOR, :Year IN number, :Month IN number, :Branch IN number, :Agency IN number) AS
BEGIN
OPEN p_recordset for
select :Year as required_year, :Month as required_month,
case when pmagt = 0 then D.branch_code else E.branch_code end as branch_code,
case when pmagt = 0 then D.branch_name else E.branch_name end as branch_name,
case when pmagt = 0 then D.region else E.region end as region,
pmagt as agency,
status || ' ' || int || ' ' || name as agent_name,
pmpol as policy_no,
pmcom as commence_date,
pmtrm as term,
pmmod as policy_mode,
case WHEN pmtbl not in (51,22,74,75,76) and ((:Year - SUBSTR (pmcom,1,4)) * 12 + to_number(:Month) - SUBSTR (pmcom,-4,2)) < 12 then 'FYR'
WHEN pmtbl not in (51,22,74,75,76) and ((:Year - SUBSTR (pmcom,1,4)) * 12 + to_number(:Month) - SUBSTR (pmcom,-4,2)) >= 12 then 'Renewal' else '' end as premium_type,
case when llprm is not null and pmprm < llprm then llprm else pmprm end as due_Premium,
case when llprm is not null then llprm else 0 end as due_paid_premium
FROM lphs.premast A left outer join lclm.ledger B on (A.pmpol = B.llpol) and (to_number(:Year || :Month) = lldue)
left outer join agent.agent C on (A.pmagt = C.agency)
left outer join BAU.SLIC_BRANCH_LIST D on (A.pmobr = D.csp_code)
left outer join BAU.SLIC_BRANCH_LIST E on (C.branch = E.csp_code)
WHERE add_months(to_date(PMCOM,'YYYYMMDD'),PMTRM*12) >= add_months( to_date(:Year || :Month || 01 ,'YYYYMMDD'),1)
and to_date(pmcom,'yyyymmdd') < to_date(:Year || :Month || 01 ,'YYYYMMDD')
and pmmod <> 5
and stid in ('Ag' , 'ME', 'Or')
and case when to_date(pmcom,'yyyymmdd') >= to_date(:Year || :Month || 01 ,'YYYYMMDD') then 'N'
when pmmod = 4 then 'Y'
when pmmod = 3 and remainder ( abs( to_number ( substr (pmcom, 5,2) ) - to_number ( :Month ) ) , 3 ) =0 then 'Y'
when pmmod = 2 and remainder ( abs( to_number ( substr (pmcom, 5,2) ) - to_number ( :Month ) ) , 6 ) =0 then 'Y'
when pmmod = 1 and remainder ( abs( to_number ( substr (pmcom, 5,2) ) - to_number ( :Month ) ) , 12 ) =0 then 'Y' else 'N' end = 'Y'
and case when pmagt = 0 then D.branch_code else E.branch_code end = :Branch
and pmagt = :Agency
END GET_RSM_LIFE_AGENT_DUES;
Error.....
Error(2,67): PLS-00049: bad bind variable 'YEAR'
Error(2,67): PLS-00103: Encountered the symbol "" when expecting one of the following: <an identifier> <a double-quoted delimited-identifier> current delete exists prior
Error(2,84): PLS-00049: bad bind variable 'MONTH'
Remove colons from within the procedure. Those are procedure's parameters, so use them as such. Pass their values as e.g.
declare
l_out sys_refcursor;
begin
GET_RSM_LIFE_AGENT_DUES (l_out, :Year, :Month, :Branch, :Agency);
end;

MERGE statement updates even if the data is not updated

I have a merge statement in which I'd like to update my table rows in case any of the columns have a different value. But it seems like even though most of the rows in the source table have remained intact, the MERGE statement performs an UPDATE on at least counts what it does an UPDATE.
DECLARE #SummaryOfChanges TABLE(Change VARCHAR(50));
MERGE MyTarget AS TARGET
USING MySource AS SOURCE
ON (SOURCE.customeridHash = TARGET.Id)
WHEN MATCHED AND (TARGET.IsCompany <> SOURCE.company
OR TARGET.Gender <> SOURCE.gender
OR TARGET.BirthDate <> CONVERT(DATE, SOURCE.dateofbirth)
OR TARGET.ZipCode <> SOURCE.ZipCode
OR TARGET.City <> SOURCE.City
OR TARGET.WantsEmail <> (CASE WHEN SOURCE.noemail = 0 THEN 1 ELSE 0 END)
OR TARGET.WantsSMS <> (CASE WHEN SOURCE.nosms = 0 THEN 1 ELSE 0 END)
OR TARGET.WantsDM <> (CASE WHEN SOURCE.nodirectmarketing = 0 THEN 1 ELSE 0 END)
OR TARGET.WantsTM <> (CASE WHEN SOURCE.notelemarketing = 0 THEN 1 ELSE 0 END)
OR TARGET.HasEmail <> SOURCE.HasEmail
OR TARGET.HasMobilePhoneNumber <> SOURCE.HasMobilePhoneNumber
OR TARGET.HasPhoneNumber = SOURCE.HasPhoneNumber
OR TARGET.Created <> SOURCE.Created
OR TARGET.Updated <> SOURCE.changed)
THEN
UPDATE SET TARGET.IsCompany = SOURCE.company,
TARGET.Gender = SOURCE.gender,
TARGET.BirthDate = CONVERT(DATE, SOURCE.dateofbirth),
TARGET.ZipCode = SOURCE.ZipCode,
TARGET.City = SOURCE.City,
TARGET.WantsEmail = (CASE WHEN SOURCE.noemail = 0 THEN 1 ELSE 0 END),
TARGET.WantsSMS = (CASE WHEN SOURCE.nosms = 0 THEN 1 ELSE 0 END),
TARGET.WantsDM = (CASE WHEN SOURCE.nodirectmarketing = 0 THEN 1 ELSE 0 END),
TARGET.WantsTM = (CASE WHEN SOURCE.notelemarketing = 0 THEN 1 ELSE 0 END),
TARGET.HasEmail = SOURCE.HasEmail,
TARGET.HasMobilePhoneNumber = SOURCE.HasMobilePhoneNumber,
TARGET.HasPhoneNumber = SOURCE.HasPhoneNumber,
TARGET.Created = SOURCE.Created,
TARGET.Updated = SOURCE.changed
WHEN NOT MATCHED BY TARGET THEN
INSERT (
Id,
IsCompany,
Gender,
BirthDate,
ZipCode,
City,
WantsEmail,
WantsSMS,
WantsDM,
WantsTM,
HasEmail,
HasMobilePhoneNumber,
HasPhoneNumber,
Created,
Updated
)
VALUES (
SOURCE.customeridHash,
SOURCE.company,
SOURCE.gender,
CONVERT(DATE, SOURCE.dateofbirth),
SOURCE.ZipCode,
SOURCE.City,
(CASE WHEN SOURCE.noemail = 0 THEN 1 ELSE 0 END),
(CASE WHEN SOURCE.nosms = 0 THEN 1 ELSE 0 END),
(CASE WHEN SOURCE.nodirectmarketing = 0 THEN 1 ELSE 0 END),
(CASE WHEN SOURCE.notelemarketing = 0 THEN 1 ELSE 0 END),
SOURCE.HasEmail,
SOURCE.HasMobilePhoneNumber,
SOURCE.HasPhoneNumber,
SOURCE.Created,
SOURCE.changed
)
WHEN NOT MATCHED BY SOURCE THEN DELETE
OUTPUT $action INTO #SummaryOfChanges;
SELECT Change, COUNT(*) CountPerChange
FROM #SummaryOfChanges
GROUP BY Change;
I do some bookkeeping at the end of the update (the final SELECT) and it seems like almost all the rows that are not new, were updated. Is this a common behavior or is there really a value amongst my <> comparison for WHEN MATCHED AND... that is updated?
Update: As suggested by one of the comments, I wrote the following test to check whether my conditions trigger an update or not:
-- TEST MERGE
select count(*)
from MyTarget TARGET join MySource SOURCE on TARGET.Id=SOURCE.customeridHash
where TARGET.IsCompany <> SOURCE.company
OR TARGET.Gender <> SOURCE.gender
OR TARGET.BirthDate <> CONVERT(DATE, SOURCE.dateofbirth)
OR TARGET.ZipCode <> SOURCE.ZipCode
OR TARGET.City <> SOURCE.City
OR TARGET.WantsEmail <> (CASE WHEN SOURCE.noemail = 0 THEN 1 ELSE 0 END)
OR TARGET.WantsSMS <> (CASE WHEN SOURCE.nosms = 0 THEN 1 ELSE 0 END)
OR TARGET.WantsDM <> (CASE WHEN SOURCE.nodirectmarketing = 0 THEN 1 ELSE 0 END)
OR TARGET.WantsTM <> (CASE WHEN SOURCE.notelemarketing = 0 THEN 1 ELSE 0 END)
OR TARGET.HasEmail <> SOURCE.HasEmail
OR TARGET.HasMobilePhoneNumber <> SOURCE.HasMobilePhoneNumber
OR TARGET.HasPhoneNumber = SOURCE.HasPhoneNumber
OR TARGET.Created <> SOURCE.Created
OR TARGET.Updated <> SOURCE.changed;
I realised that this query returns the same number of updates. So it is somehow more about the condition that the MERGE statement. But I wonder how they trigger updates.
I think I found my own mistake, in the conditions I write:
OR TARGET.HasPhoneNumber = SOURCE.HasPhoneNumber
Which almost always renders true!

SQL Server Case Statement bad performance

I have a Query with multiple CASE statements as below. It is taking taking lot of time to execute even after proper Indexing. The Sum operation on case statements causing bad performance. How can I re write the above Query to improve performance.
SELECT H.Item,
SUM(CASE A.CategeoryId
WHEN 123 THEN NULLIF(IIF(C.IsActive = 0
AND ( ( c.RowID IN ( 2, 4 )
AND H.SubId <> ISNULL(C.ItemId, 0) )
OR ( c.RowId NOT IN ( 2, 4 )
AND H.SubId = ISNULL(C.ItemId, 0) ) ), 0, A.Sales), 0)
ELSE 0
END) AS [TotalSales1],
SUM(CASE A.CategeoryId
WHEN 198 THEN NULLIF(IIF(C.IsActive = 0
AND ( ( c.RowID IN ( 2, 4 )
AND H.SubId <> ISNULL(C.ItemId, 0) )
OR ( c.RowId NOT IN ( 2, 4 )
AND H.SubId = ISNULL(C.ItemId, 0) ) ), 0, A.Sales), 0)
ELSE 0
END) AS [TotalSales2],
'',
'',
'',
'',
''
FROM SubItem AS H
INNER JOIN Item AS C WITH (NOLOCK)
ON H.Item = C.ItemId
LEFT OUTER JOIN #temp1 AS A
ON H.SubId = A.ItemId
WHERE H.Productid = 99
GROUP BY H.Item,
C.ItemId,
C.RowID

Compare two columns ftp_start_time and ftp_stop_time and return the count of columns based on conditions

[Compare two columns ftp_start_time and ftp_stop_time and return the count of columns based on conditions :3 columns are 1.Tried(count(ftp_start_time)) 2.Success(count of ftp_start_time and ftp_stop_time when not null) 3. Failed(count of ftp_stop_time must be null).. please help
Screenshot
SELECT
a.Branch_code,
b.DevCount AS total,
COUNT(a.ftp_start_time) AS tried
FROM
ftplogview AS a
LEFT JOIN
palmtecsetup AS b ON a.Branch_code = b.Branch_code
WHERE
ftp_start_date = '2016-03-31'
GROUP BY
a.Branch_code, b.DevCount
ORDER BY
Branch_code
This is the query I used so far ...can anyone help me out?
Hope this one will help you, use the sub select query like below -
SELECT
q.Branch_code,
q.DevCount AS total,
COUNT(q.ftp_start_time) AS tried,
SUM(Success) AS SuccessCount,
SUM(Failed) AS FailedCount,
SUM(NOTTRIED) AS NOTTRIEDCount
FROM
(
SELECT
a.Branch_code,
b.DevCount AS total,
a.ftp_start_time AS tried,
CASE WHEN a.ftp_start_time IS NOT NULL AND a.ftp_stop_time IS NOT NULL THEN 1 ELSE 0 END AS Success,
CASE WHEN a.ftp_start_time IS NOT NULL AND a.ftp_stop_time IS NULL THEN 1 ELSE 0 END AS Failed,
CASE WHEN a.ftp_start_time IS NULL AND a.ftp_stop_time IS NULL THEN 1 ELSE 0 END AS NOTTRIED
FROM
ftplogview AS a
LEFT JOIN
palmtecsetup AS b ON a.Branch_code = b.Branch_code
WHERE
ftp_start_date = '2016-03-31'
) Q
GROUP BY
Q.Branch_code, Q.Total
ORDER BY
Q.Branch_code
I would use Common table expression
with cte(Branch_code, DevCount, total,ftp_start_time,ftp_stop_date,ftp_stop_time )
as (
SELECT
a.Branch_code,
b.DevCount,
a.ftp_start_time,
a.ftp_stop_date,
a.ftp_stop_time
FROM
ftplogview AS a
LEFT JOIN
palmtecsetup AS b ON a.Branch_code = b.Branch_code
GROUP BY
a.Branch_code, b.DevCount
)
SELECT Branch_code,
DevCount AS total,
COUNT(ftp_start_time) AS tried,
SUM(
case
when ftp_start_time is not null and ftp_stop_time is not null then 1
else 0 end
) as success,
SUM(
case
when ftp_stop_time is null then 1
else 0 end
) as failed
FROM CTE
WHERE ftp_start_time = '2016-03-31'

How to use conditional columns values in the same select statement?

I have something like
(COMPLEX_EXPRESSION_N stands for a long subquery)
select
ID_Operation,
FirstCheck = CASE WHEN (COMPLEX_EXPRESSION_1)= 0 then 0 else 1 end,
SecondCheck = CASE WHEN (COMPLEX_EXPRESSION_2)= 0 then 0 else 1 end,
ThirdCheck = CASE WHEN (COMPLEX_EXPRESSION_3)= 0 then 0 else 1 end,
AllChecksOk = Case WHEN
(FirstCheck + SecondCheck + Third CHeck = 3)
Then 'OK' Else 'No' End
from
AllOperationsTable
Is it possible to use FirstCheck, SecondCheck, ThirdCheck as I did in the AllChecksOk line?
I am not concerned about performance, this is something that is manually run once a day on a very small number of records, I just want to avoid to create views, tables or temporary tables and keep all in a single select statement.
As an altenrative I can do this, but it makes the query less readable (as I need to write twice every complex expression):
select
ID_Operation,
FirstCheck = CASE WHEN (COMPLEX_EXPRESSION_1)= 0 then 0 else 1 end,
SecondCheck = CASE WHEN (COMPLEX_EXPRESSION_2)= 0 then 0 else 1 end,
ThirdCheck = CASE WHEN (COMPLEX_EXPRESSION_3)= 0 then 0 else 1 end,
AllChecksOk = Case WHEN
(COMPLEX_EXPRESSION_1+ COMPLEX_EXPRESSION_2+
COMPLEX_EXPRESSION_3CHeck = 3) Then 'OK' Else 'No' End
from
AllOperationsTable
You can't reference a column alias in the select but you can use a CTE as below.
;WITH CTE AS
(
select
ID_Operation,
FirstCheck = CASE WHEN (COMPLEX_EXPRESSION_1)= 0 then 0 else 1 end,
SecondCheck = CASE WHEN (COMPLEX_EXPRESSION_2)= 0 then 0 else 1 end,
ThirdCheck = CASE WHEN (COMPLEX_EXPRESSION_3)= 0 then 0 else 1 end
from
AllOperationsTable
)
SELECT *,
AllChecksOk = Case WHEN
(COMPLEX_EXPRESSION_1+ COMPLEX_EXPRESSION_2+
COMPLEX_EXPRESSION_3CHeck = 3) Then 'OK' Else 'No' End
FROM CTE
You can also use CROSS APPLY to define the 3 column aliases then reference them in the main SELECT list as in this example.
Below is a derived table solution
SELECT
T.ID_Operation,
FirstCheck = CASE WHEN T.Expr1 = 0 THEN 0 ELSE 1 END,
SecondCheck = CASE WHEN T.Expr2 = 0 THEN 0 ELSE 1 END,
ThirdCheck = CASE WHEN T.Expr3 = 0 THEN 0 ELSE 1 END,
AllChecksOk = CASE WHEN T.Expr1 + T.Expr2 + T.Expr3 = 3 THEN 'OK' ELSE 'No' END
FROM
(
SELECT
ID_Operation,
Expr1 = (COMPLEX_EXPRESSION_1),
Expr2 = (COMPLEX_EXPRESSION_2),
Expr3 = (COMPLEX_EXPRESSION_3)
FROM
AllOperationsTable
) T
Personally, I find using CTE or derived tables a bit confusing for this purpose, as you have to nest things one level and think about the nesting impliciations. A much simpler approach (at least in my opinion) is to use APPLY (or standard SQL LATERAL in other RDBMS) to generate column expression aliases:
SELECT
ID_Operation,
FirstCheck,
SecondCheck,
ThirdCheck,
AllChecksOk = CASE
WHEN FirstCheck + SecondCheck + ThirdCheck = 3 THEN 'OK' ELSE 'NO'
END
FROM
AllOperationsTable
CROSS APPLY (
SELECT
FirstCheck = CASE WHEN COMPLEX_EXPRESSION_1 = 0 THEN 0 ELSE 1 END,
SecondCheck = CASE WHEN COMPLEX_EXPRESSION_1 = 0 THEN 0 ELSE 1 END,
ThirdCheck = CASE WHEN COMPLEX_EXPRESSION_1 = 0 THEN 0 ELSE 1 END
) t

Resources