Group By syntax while using Cross Join - sql

i wish to apply a Group By condition to my SQL below so that the O/P will display POL# with GP.
select t.POl#, (DEB.CUSTD - CRED.CUSTC) AS GP
from (
(
select POL.SP_NUM POL#
, sum(D.AMT) AS CUSTD
from S_INVOICE D
, S_ASSET POL
where POL.ROW_ID = D.FN_ACCNT_ID
and POL.SP_NUM in ('000','111','222')
and D.DEBIT_TYPE = 'Customer'
group by POL.SP_NUM
) DEB
CROSS JOIN
(
select pol.SP_NUM POL#
, sum(C.AMT) AS CUSTC
from S_SRC_PAYMENT C
, S_ASSET POL
where POL.ROW_ID = C.ASSET_ID
and POL.SP_NUM in ('000','111','222')
and C.CG_DEDN_TYPE_CD = 'Customer'
group by POL.SP_NUM
) CRED
) t
group by t.POL#
When i execute the same I am getting "ORA-00933: SQL command not properly ended" error, where the cursor points to 't'
Kindly assist.
Expected O/P:
POL# GP
000 800
111 120
222 50
Appending Sample Data with explanation for better understanding of requirement:
Table 1:
S_ASSET
ROW_ID POL#
1 000
2 111
3 222
4 333
5 444
Table 2:
S_INVOICE (Debit Table)
FN_ACCNT_ID POL# DEBIT_TYPE AMT
1 000 Customer 10
1 000 Customer 10
1 000 Insurer 5
2 111 Customer 10
3 222 Customer 10
3 222 Insurer 5
5 444 Insurer 10
Table 3:
S_SRC_PAYMENT (Credit Table)
ASSET_ID POL# CG_DEDN_TYPE_CD AMT
1 000 Insurer 10
1 000 Insurer 10
1 000 Customer 5
2 111 Insurer 10
3 222 Insurer 5
3 222 Insurer 5
3 222 Customer 5
5 444 Customer 10
As per this query I will consider "Customer" records per POL# and SUM the AMT. (Every Debit for the Customer will Credit Insurer(s) per POL# & vice-versa)
Expected O/P (Sum of Debits - Sum of Credits) for a Customer per POL#
POL # AMT (GP)
000 15
111 10
222 5
333 0
444 -10

You obviously only want to get the deb and cred per s_asset and then aggregate so as to get the sums. You can do this without a join and subquery the sums directly instead:
select
sp_num as pol#,
sum(<get deb sum for the pol.row_id here>) - sum(<get cred sum for the pol.row_id here>)
from s_asset pol
where sp_num in ('000','111','222')
group by sp_num;
The complete query:
select
sp_num as pol#,
coalesce(sum(
(
select sum(deb.amt)
from s_invoice deb
where deb.fn_accnt_id = pol.row_id
and deb.debit_type = 'Customer'
)
), 0) -
coalesce(sum(
(
select sum(cred.amt)
from s_src_payment cred
where cred.asset_id = pol.row_id
and cred.cg_dedn_type_cd = 'Customer'
), 0)
) as gp
from s_asset pol
where sp_num in ('000','111','222')
group by sp_num;
The same with joins instead:
select
pol.sp_num as pol#,
coalesce(sum(deb.total), 0) - coalesce(sum(cred.total), 0) as gp
from s_asset pol
left join
(
select fn_accnt_id as pol_row_id, sum(deb.amt) as total
from s_invoice
where debit_type = 'Customer'
group by fn_accnt_id
) deb on deb.pol_row_id = pol.row_id
left join
(
select asset_id as pol_row_id, sum(amt) as total
from s_src_payment
where cg_dedn_type_cd = 'Customer'
group by asset_id
) cred on cred.pol_row_id = pol.row_id
group by pol.sp_num;

This syntax
from ( (select ...) CROSS JOIN (select ...) )
is invalid. A JOIN belongs to a FROM. So either of these would be correct:
from ( SELECT * FROM (select ...) CROSS JOIN (select ...) )
or
from (select ...) CROSS JOIN (select ...)
However, are you sure you want a CROSS JOIN at all? Both subqueries give you POL numbers plus data, bat rather than joining them on POL#, you cross join the results, so you get all POL# combinations instead.

Related

Min and max auto Id column plus other columns from the same table

I need to retrieve data from a table relative to 3 columns i.e. max and min Id for every unique reservation_Id and rnoid pair.
This is what I have tried:
SELECT
R1.*,
R2.Id, R2.Res_Id, R2.rNoid
FROM
dbo.Res_Id R1
LEFT OUTER JOIN
dbo.Res_Id R2 ON R2.rNoid = R1.rNoid
AND (R2.Id > R1.Id --min
OR (R2.Id = R1.Id
AND r2.Res_Id <> r1.Res_Id)
)
-- AND R2.rNoid <> R1.rNoid
WHERE
R2.id IS NULL
ORDER BY
R1.Id
Results:
id Res_Id, rNoid, xxx_x, yyy_x, user_id
-------------------------------------------
1 1 33 5 null 1
2 3 44 0 3 1
3 13 22 0 null 1
4 1 22 2 5 2
5 3 5 0 5 2
6 3 77 1 3 2
I am getting some unique pairs skipped.
You may try this. This will create min(id) under cte section and max(id) under ct section. After that you may easily get the inner join on Res_Id and
rNoid to get your records.
; with cte as (
SELECT r.Res_Id, r.rNoid, MIN(r.id) as MinId
FROM dbo.Res_Id R1
GROUP BY r.Res_Id, r.rNoid
),
ct as (
SELECT r.Res_Id, r.rNoid, MAX(r.id) as MaxId
FROM dbo.Res_Id R1
GROUP BY r.Res_Id, r.rNoid
)
SELECT C.Res_Id, C.rNoid, MinId, MaxId FROM CTE AS C INNER JOIN CT AS CT
ON C.Res_Id = CT.Res_Id AND C.rNoid = CT.rNoid
max and min Id for every unique reservation_Id and rnoid pair.
You seem to want a simple aggregation query:
SELECT r.Res_Id, r.rNoid, MIN(r.id), MAX(r.id)
FROM dbo.Res_Id R1
GROUP BY r.Res_Id, r.rNoid;

SQL query Group by while using Union All

I am completely New to SQL queries and need help in combining some select statements together to form one single query.
I will start one query at a time:
select sum(DOCI)
from (
select POL.SP_NUM, sum(Q.AMT + POL.AMT) DOCI
from S_ASSET POL
, S_QUOTE_ITEM Q
where POL.X_QUOTE_ID = Q.ROW_ID
and POL.SP_NUM in ('000','111','222')
group by POL.SP_NUM
UNION ALL
select POL.SP_NUM, sum(QXM.AMT) DOCI
from S_ASSET POL
, S_QUOTE_ITEM Q
, S_QUOTE_ITEM_XM QXM
where POL.X_QUOTE_ID = Q.ROW_ID
and Q.ROW_ID = QXM.PAR_ROW_ID
and POL.SP_NUM in ('000','111','222')
group by POL.SP_NUM
)
Tables:
S_ASSET
X_QUOTE_ID POL AMT
A 000 1
B 111 1
C 222 1
S_QUOTE_ITEM
ROW_ID AMT
A 10
B 10
C 1
S_QUOTE_ITEM_XM
PAR_ROW_ID AMT
A 10
B 0
Expected O/P:
POL AMT(sum)
A 21
B 11
C 2
Please help
SELECT sp_num, SUM(doci)
FROM ( ...subquery... )
GROUP BY sp_num;
The table name pol only exists inside the subquery. The outer query is selecting from the result of the subquery, not from any named table.

sql query to get data group by customer type and need to add default value if customer type not found

I have a table "Customers" with columns CustomerID, MainCountry and CustomerTypeID.
I have 5 customer types 1,2,3,4,5 .
I want to count number of customers of each country according to customer type. I am using the following query:
select count(CustomerID) as CustomerCount,MainCountry,CustomerTypeID
from Customers
group by CustomerTypeID,MainCountry
But some countries not have any customers, under type 1,2,3,4 or 5.
So I want to put a default value 0 for if customer type is not exist for that country.
Currently it is giving data as follows :-
CustomerCount MainCountry CustomerTypeID
5695 AU 1
525 AU 2
12268 AU 3
169 AU 5
18658 CA 1
1039 CA 2
24496 CA 3
2259 CA 5
2669 CO 1
10 CO 2
463 CO 3
22 CO 4
39 CO 5
As "AU" not have type 4 so I want a default value for it.
You should JOIN your table with a table with TypeId's. In this case
select count(CustomerID) as CustomerCount,TypeTable.MainCountry,TypeTable.TId
from
Customers
RIGHT JOIN (
select MainCountry,TId from
(
select Distinct MainCountry from Customers
) as T1,
(
select 1 as Tid
union all
select 2 as Tid
union all
select 3 as Tid
union all
select 4 as Tid
union all
select 5 as Tid
) as T2
) as TypeTable on Customers.CustomerTypeID=TypeTable.TId
and Customers.MainCountry=TypeTable.MainCountry
group by TypeTable.TId,TypeTable.MainCountry
Select Country.MainCountry, CustomerType.CustomerTypeId, Count(T.CustomerID) As CustomerCount
From (Select Distinct MainCountry From Customers) As Country
Cross Join (Select Distinct CustomerTypeId From Customers) As CustomerType
Left Join Customers T
On Country.MainCountry = T.MainCountry
And CustomerType.CustomerTypeId = T.CustomerTypeId
-- Edit here
And T.CreatedDate > Convert(DateTime, '1/1/2013')
-- End Edit
Group By Country.MainCountry, CustomerType.CustomerTypeId
Order By MainCountry, CustomerTypeId
Try that:
with cuntry as (
Select Distinct MainCountry From Customers
),
CustomerType as (
(Select Distinct CustomerTypeId From Customers
),
map as (
select MainCountry, CustomerTypeId from cuntry,CustomerType
)
select count(CustomerID) as CustomerCount,a.MainCountry,a.CustomerTypeID
from
map a left join Customers b on a.CustomerCount=b.CustomerCount and a.CustomerTypeID=b.CustomerTypeID

LEFT JOIN in 2 table

Why doesn't this query execute correctly?
SELECT pr.ProjectNumber,
SUM(ma.TotalAmount) As CostOfMaterials,
SUM(ot.TotalAmount) AS CostOfOthers
FROM [dbo].[tblProject] AS pr
LEFT JOIN [dbo].[tblCostOfMaterials] AS ma ON pr.ProjectNumber=ma.ProjectNumber
LEFT JOIN [dbo].[tblCostOfOthers] AS ot ON pr.ProjectNumber=ot.ProjectNumber
GROUP BY pr.ProjectNumber
I assume that you mean that because each table has multiple rows for a particular project number you count the same values multiple times. To avoid this you can use
;WITH ma
AS (select ProjectNumber,
SUM(TotalAmount) as CostOfMaterials
FROM [dbo].[tblCostOfMaterials]
GROUP BY ProjectNumber),
ot
AS (select ProjectNumber,
SUM(TotalAmount) as CostOfOthers
FROM [dbo].[tblCostOfOthers]
GROUP BY ProjectNumber)
SELECT pr.ProjectNumber,
CostOfMaterials,
CostOfOthers
FROM [dbo].[tblProject] AS pr
LEFT JOIN ma
ON pr.ProjectNumber = ma.ProjectNumber
LEFT JOIN ot
ON pr.ProjectNumber = ot.ProjectNumber
The reason why you get that behaviour is because you are getting mini Cartesian joins
WITH tblProject (ProjectNumber) AS
(
SELECT 1
),tblCostOfMaterials(ProjectNumber, TotalAmount) AS
(
SELECT 1,101 UNION ALL
SELECT 1,201 UNION ALL
SELECT 1,301
),
tblCostOfOthers(ProjectNumber, TotalAmount) AS
(
SELECT 1,100 UNION ALL
SELECT 1,200
)
SELECT *
FROM [tblProject] AS pr
LEFT JOIN [tblCostOfMaterials] AS ma ON pr.ProjectNumber=ma.ProjectNumber
LEFT JOIN [tblCostOfOthers] AS ot ON pr.ProjectNumber=ot.ProjectNumber
Returns
ProjectNumber ProjectNumber TotalAmount ProjectNumber TotalAmount
------------- ------------- ----------- ------------- -----------
1 1 101 1 100
1 1 101 1 200
1 1 201 1 100
1 1 201 1 200
1 1 301 1 100
1 1 301 1 200
You can see that the values are duplicated by the number of matching rows in the other table so the SUM will be incorrect.

How to get this result with and only with SQL?

The question is:
Two tables (t1, t2)
Table t1:
SELLER | NON_SELLER
A B
A C
A D
B A
B C
B D
C A
C B
C D
D A
D B
D C
Table t2:
SELLER | COUPON | BAL
A 9 100
B 9 200
C 9 300
D 9 400
A 9.5 100
B 9.5 20
A 10 80
Using SELECT Statement to get this result:
SELLER| COUPON | SUM(BAL)
A 9 900
B 9 800
C 9 700
D 9 600
A 9.5 20
B 9.5 100
C 9.5 120
D 9.5 120
A 10 0 # !!!
B 10 80
C 10 80
D 10 80
For seller A SUM(BAL) means sum( B.BAL,C.BAL,D.BAL), for B, SUM(BAL)=SUM(A.BAL,C.BAL,D.BAL)...
Please find a way with good performance and don't use temporary table.
My solution:
Running this query will get the result but without the row "A 10 0":
select t1.seller, t2.coupon, sum(bal)
from t1, t2
where t1.non_seller = t2.seller
group by t1.seller, t2.coupon
order by t2.coupon
Please help ~~~~~~
If I understand you correctly, you're looking for data on all sellers and all coupons. So let's start with a cross join that generates a list of coupons and sellers:
select sellers.seller
, coupons.coupon
from (
select distinct seller
from Table2
) as sellers
cross join
(
select distinct coupon
from Table2
) as coupons
For each seller-coupon combination, you're looking for the sum they can buy from other sellers. This can be accomplished by a left join:
select sellers.seller
, coupons.coupon
, case when sum(t2.bal) is null then 0 else sum(t2.bal) end
from (
select distinct seller
from Table2
) as sellers
cross join
(
select distinct coupon
from Table2
) as coupons
left join
Table2 t2
on t2.seller <> sellers.seller
and t2.coupon = coupons.coupon
group by
sellers.seller
, coupons.coupon
The only function of the case statement is to replace a null sum with a 0.
The output matches the one in your answer. Note that this solution doesn't use Table1: the list of other sellers is produced by the t2.seller <> sellers.seller condition in the left join.
I get another way to this:
select t1.seller, t2.coupon, sum(bal)
from t1, t2
where t1.non_seller = t2.seller
group by t1.seller, t2.coupon
union
(select seller,coupon,0 from t2 group by coupon having count(seller) == 1);
And I don't know if it is better or worst than compare with #Andomar :
select sellers.seller
, coupons.coupon
, case when sum(t2.bal) is null then 0 else sum(t2.bal) end
from (
select distinct seller
from Table2
) as sellers
cross join
(
select distinct coupon
from Table2
) as coupons
left join
Table2 t2
on t2.seller <> sellers.seller
and t2.coupon = coupons.coupon
group by
sellers.seller
, coupons.coupon