SQL Server SQL JOIN Assistance - sql

I'm doing joins incorrectly, and haven't figured out the proper way to get the data I need given the 3 tables below (Customer, SalesHeader, SalesDetail) I'm trying to get line items with 1 Customer per line and the sum of all Sales Details that are in GL Acct 4000 and all Sales Deails in GL Acct 5000. There are many more columns, and several other GL Accounts I want to add, but I've broken it down to the simplest form to try to get it to work. I've provide mock data below and the lastest version of the SQL Statement I had unsuccessfully worked on. It would be greatly appreciated if someone could help me figure out what I'm doing wrong in this SQL Select Statement.
Customer
CustID | CustCode
------------------
1 | AAA111
2 | AN8348
SalesHeader
SH_ID | SH_CustID | SH_GLACCT
-------------------------------
1 | 1 | 4000
2 | 1 | 5000
3 | 1 | 4000
4 | 2 | 5000
SalesDetail
SD_ID | SD_HID | Price
--------------------------
1 | 1 | 100.00
2 | 1 | 540.00
3 | 2 | 100.00
4 | 3 | 600.00
5 | 4 | 50.00
6 | 4 | 75.00
Desired OUTPUT
Carpet = 4000
Pad = 5000
CustID | CustCode | Carpet (Sum all SH_GLACCT = 4000) | PAD (Sum all SH_GLACCT = 5000)
-------------------------------------------------------------------------------------------
1 | AAA111 | 1240.00 | 100.00
2 | AN8348 | 0.00 | 125.00
Incorrect SQL (Both Amounts are over what they should be)
SELECT C.CustID, C.CustCode, SUM(ADH.Price) AS Carpet, SUM(APD.Price) As Pad
FROM Customer AS C
LEFT OUTER JOIN SalesHeader AS ACH On C.CustID = ACH.SH_CustID AND ACH.SH_GLACCT = '4000'
LEFT OUTER JOIN SalesDetail AS ADH On ACH.SH_ID = ADH.SD_HID
LEFT OUTER JOIN SalesHeader AS APH On C.CustID = APH.SH_CustID AND APH.SH_GLACCT = '5000'
LEFT OUTER JOIN SalesDetail AS APD On APH.SH_ID = APD.SD_HID
GROUP BY C.CustID, C.CustCode

Try This:
Select c.CustId, c.CustCode
Sum(Case When h.SH_GLACCT = 4000 Then Price End) Acct4000Total,
Sum(Case When h.SH_GLACCT = 5000 Then Price End) Acct5000Total
From Customer c
Join Salesheader h On h.SH_CustID = c.CustID
Join SalesDetail d On d.SD_HID = h.SH_ID
Where h.SH_GLACCT In (4000, 5000)
Group By c.CustId
if you want to list the customers with no sales then use outer join:
Select c.CustId, c.CustCode
Sum(Case When h.SH_GLACCT = 4000 Then Price End) Acct4000Total,
Sum(Case When h.SH_GLACCT = 5000 Then Price End) Acct5000Total
From Customer c
Left Join (Salesheader h Join SalesDetail d
On d.SD_HID = h.SH_ID
And h.SH_GLACCT In (4000, 5000))
On h.SH_CustID = c.CustID
Group By c.CustId

Try something like the following:
SELECT c.CustID
, c.CustCode
, Carpet = SUM(CASE WHEN sh.SH_GLACCT = 4000 THEN sd.Price ELSE 0 END)
, Pad = SUM(CASE WHEN sh.SH_GLACCT = 5000 THEN sd.Price ELSE 0 END)
FROM Customer c
LEFT JOIN
SalesHeader sh
ON c.CustID = sh.CustID
LEFT JOIN
SalesDetail sd
ON sh.sh_id = sd.sd_hid
GROUP BY
c.CustID
, c.CustCode

You can use the PIVOT operator for this:
SELECT CustID, CustCode, SUM([4000]) Carpet, SUM([5000]) PAD
FROM Cust c
JOIN SalesHeader sh ON c.CustID = sh.SH_CustID
JOIN SalesDetail sd ON sh.SH_ID = sd.SD_HID
PIVOT (
SUM(sd.Price)
FOR sh.SH_GLACCT IN ([4000],[5000])
) AS pt
GROUP BY CustID, CustCode

SELECT C.CustID, C.CustCode, SH_GLACCT, SUM(Price) AS sum_price
FROM Customer C
INNER JOIN SalesHeader sh On C.CustID = sH.SH_CustID
LEFT OUTER JOIN SalesDetail sd On sh.SH_ID = sd.SD_HID
WHERE
SH_GLACCT in(4000,5000)
GROUP BY CustID, CustCode, SH_GLACCT

Related

How to prevent duplicates when getting sum of multiple columns with multiple joins

Lets say I have 3 tables: Invoices, Charges, and Payments. Invoices can have multiple charges, and charges can have multiple payments.
Doing a simple join, data would look like this:
invoiceid | chargeid | charge | payment
----------------------------------
1 | 1 | 50 | 50
2 | 2 | 100 | 25
2 | 2 | 100 | 75
2 | 3 | 30 | 10
2 | 3 | 30 | 5
If I do an join with sums,
select invoiceid, sum(charge), sum(payment)
from invoices i
inner join charges c on i.invoiceid = c.invoiceid
inner join payments p on p.chargeid = c.chargeid
group by invoiceid
The sum of payments would be correct but charges would include duplicates:
invoiceid | charges | payments
--------------------------------------
1 | 50 | 50
2 | 260 | 115
I want a query to get a list of invoices with the sum of payments and sum of charges per invoice, like this:
invoiceid | charges | payments
--------------------------------------
1 | 50 | 50
2 | 130 | 115
Is there any way to do this by modifying the query above WITHOUT using subqueries since subqueries can be quite slow when dealing with a large amount of data? I feel like there must be a way to only include unique charges in the sum.
You can also achieve this by using LATERAL JOINS
SELECT
i.invoiceid,
chgs.total_charges,
pays.total_payments
FROM
invoices AS i
JOIN LATERAL (
SELECT
SUM( charge ) AS total_charges
FROM
charges AS c
WHERE
c.invoiceid = i.invoiceid
) AS chgs ON TRUE
JOIN LATERAL (
SELECT
SUM( payment ) AS total_payments
FROM
payments AS p
WHERE
p.chargeid = c.chargeid
) AS pays ON TRUE
one way is to do the aggregation by the tables before the joins on the grouping value
SELECT i.invoiceid, SumOfCharge, SumOfInvoice
FROM invoices i
INNER JOIN (SELECT InvoiceID, suM(charges) sumOfCharges
FROM charges c
GROUP BY Invoiceid) c
on i.invoiceid = c.invoiceid
INNER JOIN (SELECT invoiceid, sum(payment) as SumOfPayment
FROM charages c
INNER JOIN payments p on p.chargeid = c.chargeid
GROUP BY Invoiceid) P
on i.invoiceID = p.invoiceid
Another way would be to do it inline per invoice using correlation
SELECT i.invoiceid
, (SELECT SUM(charge) FROM charges c WHERE c.invoiceid = i.invoiceid) SumOfCharge
, SUM(Payment) SumOfInvoice
FROM invoices i
INNER JOIN charges c
on i.invoiceid = c.invoiceid
INNER JOIN payments p
on p.chargeid = c.chargeid
GROUP BY Invoiceid
I hope this will help.
select invoiceid, sum(distinct charge)as charges, sum(payment)as payments
from yourtable
group by invoiceid;

How do i print a row x amout of times, where x is a value in this row?

I'm filling an iReport with content of an sql query, the query in question needs to return the order row for x amount of times, where x is the value of order.qty.
this is the query in question:
select ol.order_id, i.title, i.desc, s.ean, ol.curr, ol.price, i.qty
FROM inventory i
LEFT JOIN dcsdba.sku s
ON (s.title = i.title
AND s.client_id = i.client_id)
INNER JOIN order_container oc
ON (i.client_id = oc.client_id
AND i.container_id = oc.container_id)
INNER JOIN order_header oh
ON (oh.order_id = oc.order_id
AND oh.client_id = oc.client_id)
inner join order_line ol
on (ol.order_id = oc.order_id
and ol.client_id = oh.client_id)
WHERE i.container_id = 'CONTAINER1'
AND i.client_id = 'TEST'
AND rownum <= i.qty
i.qty is 3 in this example
My current result is :
order_id | title | desc | ean | curr | price | qty
__________________________________________________
order_1 | title1| desc1| ean1|curr1 | price1| qty1
what i need is :
order_id | title | desc | ean | curr | price | qty
__________________________________________________
order_1 | title1| desc1| ean1|curr1 | price1| qty1
order_1 | title1| desc1| ean1|curr1 | price1| qty1
order_1 | title1| desc1| ean1|curr1 | price1| qty1
PS. I can't use a view or loops.
You can use the following query to achieve the same:
create table tabl(id, X) as
(select 1, 2 from dual
union all
select 2,5 from dual);
select id, x from
(select distinct id, x, level
from tabl
connect by level <= x)
order by id;
db<>fiddle demo
Cheers!!
Solved it like this:
with DETAILS AS (select
i.title,
i.Desc,
s.ean,
ol.curr,
ol.price,
i.qty
FROM inventory i
LEFT JOIN title s
ON (s.title = i.title
AND s.client_id = i.client_id)
INNER JOIN order_container oc
ON (i.client_id = oc.client_id
AND i.container_id = oc.container_id)
INNER JOIN order_header oh
ON (oh.order_id = oc.order_id
AND oh.client_id = oc.client_id)
INNER JOIN order_line ol
ON (ol.order_id = oc.order_id
AND ol.client_id = oh.client_id)
where i.container_id = 'DT1'
and i.client_id = 'TEST')
select * from DETAILS d,
(select rownum repeat from dual CONNECT BY LEVEL<=(select max(qty) from DETAILS))r
where D.qty>=r.repeat
order by d.title, r.repeat;
Credit to #Tejash for showing the connect by level

How to join result from two select query

i have two table table1 and table2 ItemId foreing key to Table 2
Table 1
|itemsId | Item | Quantity|
----------------------------
1 | item | 20
Table 2
|ItemId | Status | Quantity
---------------------------
1 |Item Out| 5
I subtract quantity in table1 - table2 and assign the value in New Column Called Remain now the problem is i want join the result column with table 1 and table2
ItemId | Item | Quantity | Remaining |
---------------------------------------
1 | item | 20 | 15
I have try This
SELECT *
FROM (select i.ItemsId,i.Date,s.[Item Name],i.Quantity,i.[Recieved By],i.[From],i.[Reference No]
from Store s inner join
ItemsMovement i
on s.ItemsId = i.ItemsId
group by i.ItemsId
) T1 INNER JOIN
(select SUM(a.Quantity - b.Quantity) AS Remaining
FROM Store a inner join
ItemsMovement b
on a.ItemsId = b.ItemsId
where b.Status ='Items In'
group by a.ItemsId
) T2
I think you can do what you want more easily with conditional aggregation:
select i.ItemsId, i.Date, s.[Item Name], i.Quantity, i.[Recieved By],
i.[From],i.[Reference No],
sum(case when i.Status = 'Items In' then s.Quantity - i.Quantity end) as remaining
from Store s inner join
ItemsMovement i
on s.ItemsId = i.ItemsId
group by i.ItemsId;

two counts in group by

I want to find for each store the number of products for that store/number of customers of that store
Customer
Id | dealerId
1 | 10
2 | 11
3 | 10
Product
Id | CustomerId
100 | 1
200 | 3
300 | 2
store
Id
10
11
The result for the above example would be:
StoreId | value
10 | 2/2
11 | 1/1
SELECT
c.dealerID as Store,
COUNT(DISTINCT c.ID) as NumOfCustomers,
COUNT(p.ID) as NumOfProducts
FROM
Customers C
INNER JOIN
Products p on p.CustomerId = c.Id
GROUP BY
c.dealerID
select S.Id AS StoreId, COUNT(p.Id) AS ProductId, COUNT(CAST.Id) AS CustomerId
from Store S
left join Customer C on C.dealerId = Scheduler.StoreId
left join Product P on P.CustomerId = CAST.Id
group by S.Id

SUM subquery for total amount for each line

I have information in a CheckLine table that essentially breaks down the various fees and information on a particular check. For this query I am wanting to SUM the total of the CheckLine but it is instead giving a SUM for ALL CheckLines together. I know I am missing something blatantly obvious, but I keep scratching my head on why I individualize the SUMs. Here is my query:
SELECT DISTINCT
O.FileNumber
,(SELECT DISTINCT
SUM(CL.Amount)
FROM
dbo.Orders O
LEFT JOIN dbo.Checks C
ON O.OrdersID = C.OrdersID
LEFT JOIN dbo.CheckLine CL
ON C.ChecksID = CL.ChecksID
) AS 'Total'
FROM
dbo.Orders O
LEFT JOIN dbo.Checks C
ON O.OrdersID = C.OrdersID
LEFT JOIN dbo.CheckLine CL
ON C.ChecksID = CL.ChecksID
This is what it is returning:
| FileNumber | … | Total |
| 1 | | 2000 |
| 2 | | 2000 |
What it should be returning is:
| FileNumber | … | Total |
| 1 | | 700 |
| 2 | | 1300 |
Thoughts on my complete brain fart here? Thanks guys!
SELECT
O.FileNumber,
O.CloseDate,
SUM(CL.Amount) as Total
FROM dbo.Orders O
LEFT JOIN dbo.Checks C
ON O.OrdersID = C.OrdersID
LEFT JOIN dbo.CheckLine CL
ON C.ChecksID = CL.ChecksID
GROUP BY O.FileNumber, O.CloseDate
When you calculate Total in a subquery, that value will be treated as constant by SQL Server that will repeat every row.
It is very common to confuse GROUP BY with DISTINCT (please look at here and here) since they return the same values if no aggregation function is in the SELECT clause. In your example:
SELECT DISTINCT FileNumber FROM ORDERS
will return the same of
SELECT FileNumber FROM ORDERS GROUP BY FileNumber
Use GROUP BY if you are wanting to aggregate information (like your field TOTAL).
You probably want this:
SELECT O.FileNumber, SUM(CL.Amount) as total
FROM dbo.Orders O
LEFT JOIN dbo.Checks C
ON O.OrdersID = C.OrdersID
LEFT JOIN dbo.CheckLine CL
ON C.ChecksID = CL.ChecksID
group by O.FileNumber