How can I order by sum of three column from different tables? - sql

select username,
(select COUNT(OpenUser) from TIcket t with(nolock) where t.OpenUser = u.UserName and cast(t.CompletedDate as date) = cast(getdate() as date) and t.IssueClass = 58) as ServiceNote,
(select COUNT(AssignUser) from TIcket t with(nolock) where ((t.AssignUser = u.UserName and t.Status = 1) and t.IssueClass != 58)) as status1,
(select COUNT(PickUpUser) from TIcket t with(nolock) where ((t.PickUpUser = u.UserName and t.Status = 2) and t.IssueClass != 58)) as status2
from users u
where isinactive = 0
and UserLevel > -1 and First_Name != ''
and center_id = '100'
I made query for my program. And I want to order by sum of three field ServiceNote,status1,status2.
I tried to order using 'Sum',(like order by (ServiceNote,status1,status2) but it doesn't work.

Doesn't this work?
order by (serviceNote + status1 + status2)
This works in most databases, but not in SQL Server. For that, use a CTE or subquery:
with cte as (
<your query here>
)
select cte.*
from cte
order by (serviceNote + status1 + status2);

Related

SQL Server query slow - how do optimize it?

I would like some help to optimize this query to be faster.
This query produces a view that latter will be showed in a table in a website.
This query is slow, and I am trying to make it faster.
The only thing I tried till now is to reduce the amount of columns I retrieve for the table.
This is the query:
SELECT TOP (100) PERCENT Id, MAX(BusinessTitle) AS BusinessTitle, MAX(ClientName) AS ClientName, MAX(ClientType) AS Type, MAX(CreatedWhen) AS CreatedWhen, MAX(CASE WHEN C.[CreatedBy] IS NULL
THEN 'Client' ELSE 'Admin' END) AS CreatedBy, CAST(MAX(CASE WHEN C.IsDisabled = 1 THEN 1 ELSE 0 END) AS BIT) AS IsDisabled, MAX(ReferenceSource) AS ReferenceSource, MAX(OtherReferenceSource)
AS OtherReferenceSource, MAX(Address) AS Address,
(SELECT MAX(T.FirstName + ' ' + T.LastName) AS Expr1
FROM dbo.ApplicationUsers AS A INNER JOIN
dbo.Therapists AS T ON A.UserName = MAX(C.ClientName) AND A.Id = T.ApplicationUserId) AS ClientAdmin,
(SELECT MAX(A.Email) AS Expr1
FROM dbo.ApplicationUsers AS A INNER JOIN
dbo.Therapists AS T ON A.UserName = MAX(C.ClientName) AND A.Id = T.ApplicationUserId) AS Email,
(SELECT MAX(Name) AS Expr1
FROM dbo.Cities AS CY
WHERE (Id = MAX(C.CityId))) AS City,
(SELECT COUNT(*) AS Expr1
FROM dbo.Patients AS P
WHERE (ClientId = C.Id)) AS TotalPatientCount,
(SELECT COUNT(*) AS Expr1
FROM dbo.Patients AS P
WHERE (ClientId = C.Id) AND (IsDeleted = 0) AND (IsDisabled = 0)) AS ActivePatientCount,
(SELECT MAX(CreatedWhen) AS Expr1
FROM dbo.Patients AS P
WHERE (ClientId = C.Id)) AS LastPatientAddition,
(SELECT COUNT(*) AS Expr1
FROM dbo.Treatments AS T
WHERE (ClientId = C.Id)) AS TotalTreatmentCount,
(SELECT MAX(CreatedWhen) AS Expr1
FROM dbo.Treatments AS T
WHERE (ClientId = C.Id)) AS LastTreatmentAddition,
(SELECT COUNT(*) AS Expr1
FROM dbo.Therapists AS T
WHERE (ClientId = C.Id)) AS TotalTherapistCount,
(SELECT COUNT(*) AS Expr1
FROM dbo.Therapists AS T INNER JOIN
dbo.ApplicationUsers AS A ON T.ClientId = C.Id AND T.ApplicationUserId = A.Id
WHERE (A.IsDeleted = 0) AND (A.IsDisabled = 0)) AS ActiveTherapistCount,
(SELECT MAX(A.CreatedWhen) AS Expr1
FROM dbo.Therapists AS T INNER JOIN
dbo.ApplicationUsers AS A ON T.ClientId = C.Id AND T.ApplicationUserId = A.Id) AS LastTherapistAddition,
(SELECT MAX(A.LastLoginDate) AS Expr1
FROM dbo.Therapists AS T INNER JOIN
dbo.ApplicationUsers AS A ON T.ClientId = C.Id AND T.ApplicationUserId = A.Id
WHERE (A.LastLoginDate IS NOT NULL)) AS TherapistLastLoginDate, CAST((CASE WHEN
((SELECT COUNT(S.[Id])
FROM [dbo].[ClientSubscriptions] AS S
WHERE ((S.[ClientId] = C.[Id]) AND (S.[IsDeleted] = 0) AND ((S.[SubscriptionEnd] IS NULL) OR
(S.[SubscriptionEnd] > GETDATE())))) > 0) THEN 1 ELSE 0 END) AS BIT) AS HasActiveSubscription,
(SELECT MAX(SubscriptionEnd) AS Expr1
FROM dbo.ClientSubscriptions AS S
WHERE (ClientId = C.Id) AND (IsDeleted = 0) AND (SubscriptionEnd IS NULL OR
SubscriptionEnd > GETDATE())) AS LastValidSubscriptionEnd, CAST((CASE WHEN
((SELECT COUNT(S.[Id])
FROM [dbo].[ClientSubscriptions] AS S
WHERE ((S.[ClientId] = C.[Id]) AND (S.[IsDeleted] = 0) AND ((S.[SubscriptionEnd] IS NULL) OR
(S.[SubscriptionEnd] > GETDATE())) AND (S.[Id] <>
(SELECT MIN(S2.[Id])
FROM [dbo].[ClientSubscriptions] AS S2
WHERE ((S2.[ClientId] = C.[Id]) AND (S2.[IsDeleted] = 0)))))) > 0) THEN 1 ELSE 0 END) AS BIT) AS IsPayingCustomer, COALESCE
((SELECT MAX(MonthlyPrice) AS Expr1
FROM dbo.ClientSubscriptions AS S
WHERE (ClientId = C.Id) AND (IsDeleted = 0) AND (SubscriptionEnd IS NULL OR
SubscriptionEnd > GETDATE()) AND (MonthlyPrice > 0)), 0.00) AS ActiveSubscriptionMonthlyPrice, MAX(ClientStatus) AS Status, MAX(Phone1) AS Phone, MAX(Phone2) AS Phone2,
(SELECT Code
FROM dbo.DiscountCoupons AS DC
WHERE (Code =
(SELECT TOP (1) DiscountCouponCode
FROM dbo.ClientPayments AS CP
WHERE (ClientId = C.Id)
ORDER BY Id))) AS DiscountCouponCode,
(SELECT IssuedTo
FROM dbo.DiscountCoupons AS DC
WHERE (Code =
(SELECT TOP (1) DiscountCouponCode
FROM dbo.ClientPayments AS CP
WHERE (ClientId = C.Id)
ORDER BY Id))) AS DiscountCouponIssuedTo,
(SELECT ClientDiscount
FROM dbo.DiscountCoupons AS DC
WHERE (Code =
(SELECT TOP (1) DiscountCouponCode
FROM dbo.ClientPayments AS CP
WHERE (ClientId = C.Id)
ORDER BY Id))) AS DiscountCouponClientDiscount, COALESCE
((SELECT COUNT(Id) AS Expr1
FROM dbo.ClientFiles AS F
WHERE (ClientId = C.Id)), 0) AS TotalFilesCount, COALESCE
((SELECT SUM(FileSize) AS Expr1
FROM dbo.ClientFiles AS F
WHERE (ClientId = C.Id)), 0) / 1048576.0 AS TotalFilesSize, CAST(MAX(CASE WHEN C.CrmEnded = 1 THEN 1 ELSE 0 END) AS BIT) AS CrmEnded, MAX(CrmStatus) AS CrmStatus, MAX(CrmUnuseReason)
AS CrmUnuseReason,
(SELECT COUNT(1) AS Expr1
FROM dbo.Tipulog_Crm_Calls_new AS CC
WHERE (Cust_id = C.Id)) AS CrmCallCount
FROM dbo.Clients AS C
WHERE (IsDeleted = 0)
GROUP BY Id
I will add a second answer, which is the complete sql. This has of course not been tested as we have no access to your data, but I think you should be able to debug it yourself. There are many pointers in this code that should show you how to go.
The basic thing is to take out all of the correlated queries and put them as subqueries. The only reason to do this is all the Max/Min you use - I would look at those as if they are not necessary depending on your data then you should take them out and join to the tables directly. All the subqueries are left joins - again make them normal joins if you can depending on your data.
Also took out the outside group by Id, as 99% sure this is not necessary, as is the Top 100% bit.
SELECT BusinessTitle, ClientName, ClientType AS Type, CreatedWhen,
CASE WHEN C.[CreatedBy] IS NULL THEN 'Client' ELSE 'Admin' END) AS CreatedBy,
CAST(CASE WHEN C.IsDisabled = 1 THEN 1 ELSE 0 END AS BIT) AS IsDisabled,
ReferenceSource, OtherReferenceSource, Address,
ApplicationByName.FullName AS ClientAdmin,
ApplicationByName.Email AS Email,
Cities.Name AS City,
Patients.TotalPatientCount,
Patients.ActivePatientCount,
Patients.LastPatientAddition,
Treatments.TotalTreatmentCount,
Treatments.LastTreatmentAddition,
Therapists.TotalTherapistCount,
Therapists.ActiveTherapistCount,
Therapists.LastTherapistAddition,
Therapists.TherapistLastLoginDate
CAST(CASE WHEN Subscriptions.SubscriptionCount>0 then 1 else 0 end as BIT) as HasActiveSubscription,
Subscriptions.LastValidSubscriptionEnd
CAST(Subscriptions.IsPayingCustomer AS BIT) AS IsPayingCustomer,
COALESCE(ActiveSubscriptionMonthlyPrice,0) as ActiveSubscriptionMonthlyPrice
ClientStatus AS Status, Phone1 AS Phone, Phone2 AS Phone2,
ClientPayments.DiscountCouponCode,
DiscountCoupons.IssuedTo AS DiscountCouponIssuedTo,
DiscountCoupons.ClientDiscount AS DiscountCouponClientDiscount,
COALESCE(ClientFiles.TotalFilesCount,0) AS TotalFilesCount,
COALESCE(ClientFiles.TotalFilesSize,0) AS TotalFilesSize,
CAST((CASE WHEN C.CrmEnded = 1 THEN 1 ELSE 0 END) AS BIT) AS CrmEnded,
CrmStatus, CrmUnuseReason,
Crm_Calls.CrmCallCount
FROM dbo.Clients AS C
left join (
select A.UserName,
max(T.FirstName + ' ' + T.LastName) as FullName,
max(A.Email) as Email
from dbo.ApplicationUsers A
join dbo.Therapists T on T.ApplicationUserId=A.Id
group by A.Username
) ApplicationByName on ApplicationByName.UserName=C.ClientName
join dbo.Cities on Cities.ID=c.CityID
left join (
SELECT ClientId,
COUNT(*) AS TotalPatientCount,
sum(case when IsDeleted = 0 AND IsDisabled = 0 then 1 else 0 end) AS ActivePatientCount,
MAX(CreatedWhen) AS LastPatientAddition
FROM dbo.Patients
GROUP BY ClientId
) Patients on Patients.ClientId = C.Id
left join (
SELECT ClientId,
COUNT(*) AS TotalTreatmentCount,
MAX(CreatedWhen) AS LastTreatmentAddition
FROM dbo.Treatments
GROUP BY ClientId
) Treatments on Treatments.ClientID = C.Id
left join (
select T.ClientId,
count(distinct T.Id) as TotalTherapistCount,
sum(case when A.IsDeleted = 0 AND A.IsDisabled = 0 then 1 else 0 end) as ActiveTherapistCount,
max(A.CreatedWhen) as LastTherapistAddition,
max(A.LastLoginDate) as TherapistLastLoginDate
from Therapists T
left join dbo.ApplicationUsers A on A.Id=T.ApplicationUserId
group by T.ClientId
) Therapists on Therapists.ClientID = C.Id
left join (
SELECT S.ClientId,
count(*) as SubscriptionCount,
MAX(SubscriptionEnd) as LastValidSubscriptionEnd,
MAX(case when MinSub.Id!=S.ID then 1 else 0 end as IsPayingCustomer,
max(case when MonthlyPrice>0 then 0 end) as ActiveSubscriptionMonthlyPrice
FROM dbo.ClientSubscriptions S
join (
select ClientId, min(Id) as Id
from dbo.ClientSubscriptions
where IsDeleted=0
group by ClientId
) MinSub on MinSub.ClientId=ClientSubscriptions.ClientId
where IsDeleted=0 and (SubscriptionEnd is null or SubscriptionEnd>getdate())
group by ClientId
) Subscriptions on Subscriptions.ClientId=C.Id
left join (
select ClientId,
DiscountCouponCode,
row_number() over(partition by ClientId, order by Id) rn
from dbo.ClientPayments
) ClientPayments on ClientPayments.ClientId=C.ID and rn=1
left join dbo.DiscountCoupons on DiscountCoupons.Code=ClientPayments.DiscountCouponCode
left join (
select ClientId,
count(*) as TotalFilesCount,
sum(FileSize)/1048576.0 as TotalFilesSize
from dbo.ClientFiles
group by ClientId
) ClientFiles on ClientFiles.ClientId=Client.Id
left join (
SELECT Cust_id, COUNT(1) AS CrmCallCount
FROM dbo.Tipulog_Crm_Calls_new
group by Cust_id
) Crm_Calls on Crm_Calls.Cust_id=C.Id
WHERE C.IsDeleted = 0
This is a very partial answer, but you asked how to refer to a table once instead of multiple times in multiple subqueries.
This is an example of how you would replace all those subqueries to the Patients & Treatments tables, and also the cities table. You really need to learn about joins.
FROM dbo.Clients AS C
join dbo.Cities on Cities.ID=c.CityID
left join (
SELECT ClientId,
COUNT(*) AS TotalPatientCount,
sum(case when IsDeleted = 0 AND IsDisabled = 0 then 1 else 0 end) AS ActivePatientCount,
MAX(CreatedWhen) AS LastPatientAddition
FROM dbo.Patients
GROUP BY ClientId
) Patients on Patients.ClientId = C.Id
left join (
SELECT ClientId,
COUNT(*) AS TotalTreatmentCount,
MAX(CreatedWhen) AS LastTreatmentAddition
FROM dbo.Treatments
GROUP BY ClientId
) Treatments on Treatments.ClientID = C.Id
Then your column list replaces the subqueries to Patients and City like this:
Cities.Name AS City,
Patients.TotalPatientCount,
Patients.ActivePatientCount,
Patients.LastPatientAddition,
Treatments.TotalTreatmentCount,
Treatments.LastTreatmentAddition,
That should at least give you an idea.

Query for count and distinct

I should make a report in T-SQL from several table.
I can join all the table needed but after I don't know excatly how to get my information.
Explanation :
I've got the following table :
Tbl_User (UserId, Username)
Tbl_Customer (CustomeriD, CustomerName)
Tbl_DocA (DocId, CustomerID, DateCreate, DateAdd, UseriD)
Tbl_DocB (DocId, CustomerID, DateCreate, DateAdd, UseriD)
Tbl_DocC (DocId, CustomerID, DateCreate, DateAdd, UseriD)
I am trying to get a report like this :
After I can get this, the idea is to have a filter with the date in SQL reporting.
You can union all the document tables together and join users and customers on it.
SELECT Customer.CustomerID
,Customer.CustomerName
,COUNT(CASE WHEN DocType = 'A' THEN 1 END) AS doc_a_total
,COUNT(CASE WHEN DocType = 'B' THEN 1 END) AS doc_b_total
,COUNT(CASE WHEN DocType = 'C' THEN 1 END) AS doc_c_total
,COUNT(CASE WHEN DocType = 'A' AND user.username ='azerty' THEN 1 END) AS doc_a_made_by_azerty
,COUNT(CASE WHEN DocType = 'B' AND user.username ='azerty' THEN 1 END) AS doc_b_made_by_azerty
,COUNT(CASE WHEN DocType = 'C' AND user.username ='azerty' THEN 1 END) AS doc_c_made_by_azerty
FROM (
(SELECT 'A' AS DocType, * FROM Tbl_DocA)
UNION ALL
(SELECT 'B' AS DocType, * FROM Tbl_DocB)
UNION ALL
(SELECT 'C' AS DocType, * FROM Tbl_DocC)
) AS docs
JOIN Tbl_User AS user ON user.UserId = docs.UseriD
JOIN Tbl_Customer AS Customer ON Customer.CustomeriD = docs.CustomeriD
GROUP BY Customer.CustomerID , Customer.CustomerName
You can use common table expressions to get the count for each report type per customer, with conditional aggregation for reports made by a specific user, and join them to the customers table.
Something like this should get you the desired results:
DECLARE #UserId int = 1; -- or whatever the id of the user you need
WITH CTEDocA AS
(
SELECT CustomerID
, COUNT(DocId) As NumberOfReports
, COUNT(CASE WHEN UserId = #UserId THEN 1 END) As NumberOfReportsByUserAzerty
FROM Tbl_DocA
GROUP BY CustomerID
), CTEDocB AS
(
SELECT CustomerID
, COUNT(DocId) As NumberOfReports
, COUNT(CASE WHEN UserId = #UserId THEN 1 END) As NumberOfReportsByUserAzerty
FROM Tbl_DocB
GROUP BY CustomerID
), CTEDocC AS
(
SELECT CustomerID
, COUNT(DocId) As NumberOfReports
, COUNT(CASE WHEN UserId = #UserId THEN 1 END) As NumberOfReportsByUserAzerty
FROM Tbl_DocC
GROUP BY CustomerID
)
SELECT cust.CustomeriD
,cust.CustomerName
,ISNULL(a.NumberOfReports, 0) As NumberOfDocA
,ISNULL(a.NumberOfReportsByUserAzerty, 0) As NumberOfDocAByAzerty
,ISNULL(b.NumberOfReports, 0) As NumberOfDocB
,ISNULL(b.NumberOfReportsByUserAzerty, 0) As NumberOfDocBByAzerty
,ISNULL(c.NumberOfReports, 0) As NumberOfDocC
,ISNULL(c.NumberOfReportsByUserAzerty, 0) As NumberOfDocCByAzerty
FROM Tbl_Customer cust
LEFT JOIN CTEDocA As a
ON cust.CustomeriD = a.CustomerID
LEFT JOIN CTEDocA As b
ON cust.CustomeriD = b.CustomerID
LEFT JOIN CTEDocA As c
ON cust.CustomeriD = c.CustomerID
To filter by date you can add a where clause to each common table expresstion.
BTW, The fact that you have three identical tables for three document types suggest a bad database design.
If these tables are identical you should consider replacing them with a single table and add a column to that table describing the document type.
There are several ways to do this. One key feature needed is to count a particular user apart from the others. This is done with conditional aggregation. E.g.:
select
customerid,
count(*),
count(case when userid = <particular user ID here> then 1 end)
from tbl_doca
group by customerid;
Here is one possible query using a cross join to get the user in question once and cross apply to get the numbers.
select
c.customerid,
c.customername,
doca.total as doc_a_total,
doca.az as doc_a_by_azerty,
docb.total as doc_b_total,
docb.az as doc_b_by_azerty,
docc.total as doc_c_total,
docc.az as doc_c_by_azerty
from tbl_customer c
cross join
(
select userid from tbl_user where username = 'Azerty'
) azerty
cross apply
(
select
count(*) as total,
count(case when da.userid = azerty.userid then 1 end)n as az
from tbl_doca da
where da.customerid = c.customerid
) doca
cross apply
(
select
count(*) as total,
count(case when db.userid = azerty.userid then 1 end)n as az
from tbl_docb db
where db.customerid = c.customerid
) docb
cross apply
(
select
count(*) as total,
count(case when dc.userid = azerty.userid then 1 end)n as az
from tbl_docc dc
where dc.customerid = c.customerid
) docc
order by c.customerid;
Other options would be to replace the cross apply with left outer join and non-correlated subqueries or to put subqueries into the select clause.
Combining the totals for the documents is another method.
Then use conditional aggregation for the counts.
untested notepad scribble:
;WITH SPECIFICUSER AS
(
SELECT UseriD
FROM Tbl_User
WHERE UserName = 'azerty'
),
DOCTOTALS (
SELECT CustomeriD, UseriD, 'DocA' AS Src, COUNT(DocId) AS Total
FROM Tbl_DocA
GROUP BY CustomeriD, UseriD
UNION ALL
SELECT CustomeriD, UseriD, 'DocB', COUNT(DocId)
FROM Tbl_DocB
GROUP BY CustomeriD, UseriD
UNION ALL
SELECT CustomeriD, UseriD, 'DocC', COUNT(DocId)
FROM Tbl_DocC
GROUP BY CustomeriD, UseriD
)
SELECT
docs.CustomeriD,
cust.CustomerName,
SUM(CASE WHEN usrX.UseriD is not null AND docs.Src = 'DocA' THEN docs.Total ELSE 0 END) AS Total_DocA_userX,
SUM(CASE WHEN Src = 'DocA' THEN docs.Total ELSE 0 END) AS Total_DocA,
SUM(CASE WHEN usrX.UseriD is not null AND docs.Src = 'DocB' THEN docs.Total ELSE 0 END) AS Total_DocB_userX,
SUM(CASE WHEN Src = 'DocB' THEN docs.Total ELSE 0 END) AS Total_DocB,
SUM(CASE WHEN usrX.UseriD is not null AND docs.Src = 'DocC' THEN docs.Total ELSE 0 END) AS Total_DocC_userX,
SUM(CASE WHEN Src = 'DocC' THEN docs.Total ELSE 0 END) AS Total_DocC
FROM DOCTOTALS docs
LEFT JOIN Tbl_Customer cust ON cust.CustomeriD = docs.CustomeriD
LEFT JOIN Tbl_User usr ON usr.UseriD = docs.UseriD
LEFT JOIN SPECIFICUSER usrX ON usrX.UseriD = docs.UseriD
GROUP BY docs.CustomeriD, cust.CustomerName
ORDER BY docs.CustomeriD
Those long column names could be set on the report side

Problem with a query in Postgres that I want to move to SQL Server

I have the following query in postgres that I now need to run on SQL Server. Obviously I have already changed the trunc per round and basic things, but mainly I have a problem in the principle select distinct on (c.cod_socio) tbl. * Since SQL Server does not recognize that syntax.
select distinct on (c.cod_socio)
tbl.*, h.cod_oficina, h.cod_transaccion, h.num_transaccion,
h.num_sec, h.fec_movimiento
from
sgf_det_mov_his h
inner join
sgf_cuenta c on c.cod_producto = h.cod_producto and c.cod_cuenta = h.cod_cuenta
inner join
sgf_tran t on t.cod_transaccion = h.cod_transaccion and t.cod_oficina = h.cod_oficina and t.cod_tipo_transaccion in ('DA', 'DP','NC')
inner join
(select
sgf_cuenta.cod_socio,
sum(trunc(sgf_det_mov_his.val_efectivo, 0) + trunc(sgf_det_mov_his.val_cheques, 0)) as total
from
sgf_det_mov_his, sgf_cuenta, sgf_tran
where
sgf_cuenta.cod_producto = sgf_det_mov_his.cod_producto
and sgf_cuenta.cod_cuenta = sgf_det_mov_his.cod_cuenta
and sgf_det_mov_his.sts_mov = 'A'
and sgf_tran.cod_transaccion = sgf_det_mov_his.cod_transaccion
and sgf_tran.cod_oficina = sgf_det_mov_his.cod_oficina
and sgf_cuenta.cod_producto <> 2
and sgf_tran.cod_tipo_transaccion in ('DA', 'DP','NC')
and isnull(sgf_tran.cod_uaf, 0) > 0
and isnull(sgf_tran.cod_uaf, 0) not in (71)
and sgf_cuenta.cod_cuenta not in (select cod_cuenta
from sgf_credito
where sgf_credito.cod_producto = sgf_cuenta.cod_producto
and sgf_credito.cod_cuenta = sgf_cuenta.cod_cuenta
and sts_operacion in ('A'))
and date(sgf_det_mov_his.fec_movimiento) between '2015-01-01' and '2019-01-01'
group by
sgf_cuenta.cod_socio
having
sum(trunc(sgf_det_mov_his.val_efectivo,0) + trunc(sgf_det_mov_his.val_cheques,0)) >= 5000) tbl on tbl.cod_socio = c.cod_socio
where
date(h.fec_movimiento) between '2015-01-01' and '2019-01-01'
order by
c.cod_socio, h.fec_movimiento desc
distinct on (...) simply retains the "first row" which may be emulated using row_number() over(...) and a following where clause predicate that limits to one row per partition.. Note that distinct on relies on the order by clause to decide the "first row", so you need the equivalent conditions in the over clause. Also note if you after greater query compatibility between the two databases you could use the same row_number approach in PostgreSQL.
SELECT
*
FROM (
SELECT
ROW_NUMBER() OVER (PARTITION BY c.cod_socio ORDER BY h.fec_movimiento DESC) AS rn
, c.cod_socio
, tbl.*
, h.cod_oficina
, h.cod_transaccion
, h.num_transaccion
, h.num_sec
, h.fec_movimiento
FROM sgf_det_mov_his h
INNER JOIN sgf_cuenta c ON c.cod_producto = h.cod_producto
AND c.cod_cuenta = h.cod_cuenta
INNER JOIN sgf_tran t ON t.cod_transaccion = h.cod_transaccion
AND t.cod_oficina = h.cod_oficina
AND t.cod_tipo_transaccion IN ('DA', 'DP', 'NC')
INNER JOIN (
SELECT
sgf_cuenta.cod_socio
, SUM(trunc(sgf_det_mov_his.val_efectivo, 0) + trunc(sgf_det_mov_his.val_cheques, 0)) AS total
FROM sgf_det_mov_his
, sgf_cuenta
, sgf_tran
WHERE sgf_cuenta.cod_producto = sgf_det_mov_his.cod_producto
AND sgf_cuenta.cod_cuenta = sgf_det_mov_his.cod_cuenta
AND sgf_det_mov_his.sts_mov = 'A'
AND sgf_tran.cod_transaccion = sgf_det_mov_his.cod_transaccion
AND sgf_tran.cod_oficina = sgf_det_mov_his.cod_oficina
AND sgf_cuenta.cod_producto <> 2
AND sgf_tran.cod_tipo_transaccion IN ('DA', 'DP', 'NC')
AND ISNULL(sgf_tran.cod_uaf, 0) > 0
AND ISNULL(sgf_tran.cod_uaf, 0) NOT IN (71)
AND sgf_cuenta.cod_cuenta NOT IN (
SELECT
cod_cuenta
FROM sgf_credito
WHERE sgf_credito.cod_producto = sgf_cuenta.cod_producto
AND sgf_credito.cod_cuenta = sgf_cuenta.cod_cuenta
AND sts_operacion IN ('A')
)
AND DATE(sgf_det_mov_his.fec_movimiento) BETWEEN '2015-01-01' AND '2019-01-01'
GROUP BY
sgf_cuenta.cod_socio
HAVING SUM(trunc(sgf_det_mov_his.val_efectivo, 0) + trunc(sgf_det_mov_his.val_cheques, 0)) >= 5000
) tbl ON tbl.cod_socio = c.cod_socio
WHERE DATE(h.fec_movimiento) BETWEEN '2015-01-01' AND '2019-01-01'
) AS d
WHERE d.rn = 1
ORDER BY
d.cod_socio
, d.fec_movimiento DESC

How do I compare SUM and COUNT()s in SQL?

I am building a small query to find all CustomerNumbers where all of their policies are in a certain status (terminated).
Here is the query I am working on
select
a.cn
,p.pn
, tp = COUNT(p.pn)
, tp2 = SUM(case when p.status = 4 then 1 else 0 end)
from
(
select cn, cn2
from bc
union
select cn, cn2= fn
from ic
) as a
left join p as p
on a.cn = p.cn
group by
a.cn,
pn
My issue is when I add the clause:
WHERE cn = tp
It says the columns are invalid. Am I missing something incredibly obvious?
You can't use aliases at the same level of a query. The reason is that the where clause is logically evaluated before the select, so the aliases defined in the select are not available in the where.
A typical solution is to repeat the expression (other answers) or use a subquery or cte:
with cte as (
<your query here>
)
select cte.*
from cte
where TotalPolicies = TermedPolicies;
However, in your case, you have an easier solution, because you have an aggregation query. So just use:
having TotalPolicies = TermedPolicies
You cannot use the aliased aggregate column names in the where clause. You have to use the expression itself instead. Also you cannot use it as where cluase, but use it in the having clause
HAVING COUNT(p.PolicyNumber) = SUM(case when p.status = 4 then 1 else 0 end)
You can also make the whole query as a subquery then add your where statement:
select CustomerNumber
,PolicyNumber
,TotalPolicies
,TermedPolicies
from (
select
a.CustomerNumber
,p.PolicyNumber
, TotalPolicies = COUNT(p.PolicyNumber)
, TermedPolicies = SUM(case when p.status = 4 then 1 else 0 end)
from
(
select CustomerNumber, CompanyName
from BusinessClients
union
select CustomerNumber, CompanyName = FullName
from IndividualClients
) as a
left join Policies as p
on a.CustomerNumber = p.CustomerNumber
group by
a.CustomerNumber,
PolicyNumber
) tb
where TotalPolicies = TermedPolicies
select
a.CustomerNumber
,p.PolicyNumber
, COUNT(p.PolicyNumber) as TotalPolicies
, SUM(case when p.status = 4 then 1 else 0 end) as TermedPolicies
from
(
select CustomerNumber, CompanyName
from BusinessClients
union
select CustomerNumber, CompanyName = FullName
from IndividualClients
) as a
left join Policies as p
on a.CustomerNumber = p.CustomerNumber
WHERE COUNT(p.PolicyNumber)= SUM(case when p.status = 4 then 1 else 0 end)
group by
a.CustomerNumber,
PolicyNumber
This should work. But it is not tested.
In order to filter by an aggregate function, you must include it in the HAVING clause, rather than the WHERE clause.
select
a.CustomerNumber
,p.PolicyNumber
, TotalPolicies = COUNT(p.PolicyNumber)
, TermedPolicies = SUM(case when p.status = 4 then 1 else 0 end)
from
(
select CustomerNumber, CompanyName
from BusinessClients
union
select CustomerNumber, CompanyName = FullName
from IndividualClients
) as a
left join Policies as p
on a.CustomerNumber = p.CustomerNumber
having COUNT(p.PolicyNumber) = SUM(case when p.status = 4 then 1 else 0 end)
group by
a.CustomerNumber,
PolicyNumber
The reason for this has to do with the way SQL engines evaluate queries. The contents of the WHERE clause are used to filter out rows before the aggregate functions are applied. If you could reference aggregate functions there, the engine would have to have some way to determine which predicates to apply before aggregation and which to apply after. The HAVING clause allows the engine to have a clear demarcation between the two: WHERE applies before aggregation and HAVING applies after aggregation.
When dealing with aggregations in a query that has grouping, you will need to use HAVING. This should work:
select
a.CustomerNumber
,p.PolicyNumber
, TotalPolicies = COUNT(p.PolicyNumber)
, TermedPolicies = SUM(case when p.status = 4 then 1 else 0 end)
from
(
select CustomerNumber, CompanyName
from BusinessClients
union
select CustomerNumber, CompanyName = FullName
from IndividualClients
) as a
left join Policies as p
on a.CustomerNumber = p.CustomerNumber
group by
a.CustomerNumber,
PolicyNumber
HAVING TermedPolicies = SUM(case when p.status = 4 then 1 else 0 end)

Correlated query: select where condition not max(condition in inner query)

I am trying to select all the rows where the userName and groupId is duplicated, and the userId is not the max userId for that userName/groupId combination. Here is my code so far:
select *
from userTable u
where exists
(select *
from userTable u1
where userName <> '' and userName is not null
and u.userName = u1.userName and u.groupId = u1.groupId
and u.userId <> max(u1.userId)
group by userName, groupId
having count(*) > 1)
order by userName
However, the line:
and u.userId <> u1.max(userId)
is giving me an error.
What is the right way to do this query?
SELECT u.*
FROM (
SELECT userName, groupId, MAX(userId) AS maxId
FROM userTable
GROUP BY
userName, groupId
HAVING COUNT(*) > 1
) q
JOIN userTable u
ON u.userName = q.userName
AND u.groupId = q.groupId
AND u.userId <> q.maxId
This should do it, I think:
select t.*
from dbo.UserTable t
join ( select userName , groupID , maxUserID = max(userID)
from dbo.UserTable x
group by userName , groupID
having count(*) > 1
) dupes on dupes.userName = t.userName
and dupes.groupID = t.groupID
and dupes.maxUserID > t.userID