left join to get sum of column in second table - sql-server

How to get sum of Collection.Amount from table Collection provided that there aren't entries for all JId but only for those JId which have some payment against them
ALTER PROCEDURE dbo.test
AS
select JobCard.JId, sum(cast(isnull(Collection.Amount, 0) as bigint)) as [Amount]
from JobCard left join Collection on JobCard.JId=Collection.JId
group by JobCard.JId, Collection.Amount
order by JobCard.JId

Change your GROUP BY clause to:
group by JobCard.JId

I think you should write your query like this, This will give you perfect result.
ALTER PROCEDURE dbo.test
AS
SELECT J.JId,
SUM(CAST(ISNULL(C.Amount, 0) AS BIGINT)) AS [Amount]
FROM JobCard J
LEFT JOIN COLLECTION C
ON J.JId = C.JId
GROUP BY
J.JId
ORDER BY
J.JId
Need to remove Collection.Amount from group by clause.

Related

Update Table with Duplicates

My Table, #MetaData looks like this
Table_Name Element Join_prefix
Incident hold_reason h
Incident impact i
Incident incident_state i
Incident notify n
Incident severity s
Incident state s
Change impact i
Change incident_state i
I want to update the join_prefix where it is the same, to the first 2 characters of the element, within the Table_Name. So the table looks like
Table_Name Element Join_prefix
Incident hold_reason h
Incident impact im
Incident incident_state in
Incident notify n
Incident severity se
Incident state st
Change impact im
Change incident_state in
I've been using the following sql but it updates all the rows
update #MetaData
set join_prefix=substring(element,1,2)
where exists(
select [Table_Name],[Join_prefix]
from #MetaData
group by [Table_Name],[Join_prefix]
having count(join_prefix)>1)
One method would be to use an updatable CTE. Within the CTE you can use a windowed COUNT to count how many rows have the same prefix, and then update those rows:
SELECT *
INTO dbo.YourTable
FROM (VALUES('Incident','hold_reason ',CONVERT(varchar(4),'h')),
('Incident','impact ',CONVERT(varchar(4),'i')),
('Incident','incident_state',CONVERT(varchar(4),'i')),
('Incident','notify ',CONVERT(varchar(4),'n')),
('Incident','severity ',CONVERT(varchar(4),'s')),
('Incident','state ',CONVERT(varchar(4),'s')),
('Change ','impact ',CONVERT(varchar(4),'i')),
('Change ','incident_state',CONVERT(varchar(4),'i')))V(Table_Name,Element,Join_prefix)
GO
WITH CTE AS(
SELECT Element,
Join_prefix,
COUNT(Join_prefix) OVER (PARTITION BY Join_prefix) AS C
FROM dbo.YourTable)
UPDATE CTE
SET Join_prefix = LEFT(Element,2)
WHERE C > 1;
GO
SELECT *
FROM dbo.YourTable;
GO
DROP TABLE dbo.YourTable;
Your subquery is not correlated to the outside, so it always returns true. You need a WHERE
Note that exists doesn't need to select anything, you can select 1.
Also count(*) and count(non_null_value) is the same
update m1
set join_prefix = substring(element, 1, 2)
from #MetaData m1
where exists (select 1
from #MetaData m2
where m2.join_prefix = m1.join_prefix
group by m2.Table_Name, m2.Join_prefix
having count(*) > 1
);
A better method would be an updatable CTE
with CTE as (
select *,
cnt = count(*) over (partition by m.Table_Name, m.join_prefix)
from #MetaData m
)
update CTE
set join_prefix = substring(element,1,2)
from #MetaData m1
where t.cnt > 1;

How to Combine Three table with aggregate functions in Sql Server

I have three tables one is SaleinfoTB second table is StarReadingEndReading and 3rd table is ExpenseinfoTb in the SaleinfoTB table i want to sum TotalBill Column and StartReadingEndReading table i want to sum ActualSell Column and Plus both Sum and mins from ExpenseintoTB Table Column Amount
select SUM((SaleinfoTB.TotalBill)+ SUM(StartReadingEndReading.ActualSell)) -
(SUM(ExpenseinfoTB.Amount)) from SaleinfoTB,SaleinfoTB,ExpenseinfoTB
where SaleinfoTB.Date=StartReadingEndReading.ReadingDate
and ExpenseinfoTB.ExpenseDate=SaleinfoTB.Date
and ExpenseinfoTB.ExpenseDate=StartReadingEndReading.ReadingDate
I think what you are getting at is how to get the sum of each item before you do the addition/subtraction
SELECT (ISNULL(SaleinfoTB.SumTotalBill,0)
+ ISNULL(StartReadingEndReading.SumActualSell,0))
- ISNULL(ExpenseinfoTB.SumAmount,0)
FROM
(SELECT SUM(TotalBill) SumTotalBill, [Date]
FROM SaleinfoTB
Group By [Date])SaleinfoTB
INNER JOIN
(SELECT SUM(ActualSell) SumActualSell, ReadingDate
FROM StartReadingEndReading
Group By ReadingDate)StartReadingEndReading
ON SaleinfoTB.[Date]=StartReadingEndReading.ReadingDate
INNER JOIN
(SELECT SUM(Amount) SumAmount, ExpenseDate
FROM ExpenseinfoTB
Group By ExpenseDate)ExpenseinfoTB
ON ExpenseinfoTB.ExpenseDate=SaleinfoTB.[Date]

Create a trigger update a field in a table from Total of Line Items in another table

I have two table Order and Order_Details. I would like to create a trigger that would update the Order.Order_Total by adding the Order_Details.Price fields that belong to that specific order. Here's what I have so far but its giving me the following error
Subquery returned more than 1 value. This is not permitted when the subquery follows
Update Order
Set Order_Total =
(Select SUM(Price)
From Order_Details
Group By Order_Id)
From Order_Details
Try this.. The issue is in your sub query which does not have any binding with the order table.
UPDATE o
SET o.Order_Total = t.tprice
FROM Order o
LEFT JOIN ( SELECT Order_Id, SUM(isnull(price,0)) tprice
FROM OrderDetails
GROUP BY Order_Id) t
ON o.Order_Id=t.Order_Id
Ok here's what I ended up doing in case someone has the same question. I created a CTE to add the Order_Details price and I updated the Order.Total from that CTE. Here's the full code I used.
IF EXISTS ( SELECT 1 FROM sys.triggers WHERE object_id = object_id('dbo.trOrder_Details_AIU') )
DROP TRIGGER dbo.trOrder_Details_AIU
GO
CREATE TRIGGER dbo.trOrder_Details_AIU
ON dbo.Order_Details
AFTER INSERT,UPDATE, Delete
AS
BEGIN
set nocount on;
begin
; with Total_CTE(Order_Id, Total)
as
(
Select Order_Id, SUM(Price)
From Order_Details
Group By Order_Id
)
Update Order
Set Order_Total = Total_CTE.Amount
From Total_CTE
Where Total_CTE.Order_Id = Total.Order_Id
end
END

How to use 'Merge' to combine rows into a single one

Scenario:
I have a simplified version of a result set obtained from a series of complex joins. I have placed the result set in a temporary table. The result set consists of records of activity/activities in a day.
I need to join the 2 rows (merge activities of a day into a single row) with similar dates so that the resulting result set would be
I am trying to make this work
Merge #temp as target
using #temp as source
on (target.Date = source.Date) and target.Writing is NULL
when matched then
update set target.Writing = source.Writing;
but I'm running into this error:
The MERGE statement attempted to UPDATE or DELETE the same row more
than once. This happens when a target row matches more than one source
row. A MERGE statement cannot UPDATE/DELETE the same row of the target
table multiple times. Refine the ON clause to ensure a target row
matches at most one source row, or use the GROUP BY clause to group
the source rows.
What code modifications can you suggest?
This should do it:
SELECT dfl.mydate, dfl.firststart, dfl.lastend, fa.ActivityA, sa.ActivityB
FROM
(select s.mydate, firststart, lastend FROM
(SELECT mydate, MIN(starttime) as firststart from target GROUP by mydate) s
iNNER JOIN
(SELECT mydate, MAX(EndTime) as lastend from target GROUP by mydate) e
ON s.mydate = e.mydate) AS dfl
INNER JOIN
target fa on dfl.mydate = fa.mydate and dfl.firststart = fa.starttime
INNER JOIN
target sa on dfl.mydate = sa.mydate and dfl.lastend = sa.EndTime
Please note for my test I have called my table target and the columns: mydate, starttime, endtime, activitya and activityb.
No need to merge, a (relatively) simple select yields the results you want.
HTH
PS It helps when using time data to use a 24 hour clock. I have assumed by 5:00 you really meant 17:00
You don't need MERGE statement.
DECLARE #Test TABLE ([Id] int, [Date] nvarchar(10), [TimeIn] nvarchar(10), [TimeOut] nvarchar(10), [Reading] nvarchar(10), [Writeing] nvarchar(10))
INSERT INTO #Test
VALUES
(1,'08-01','8:00','5:00','Y',NULL),
(2,'08-02','8:00','5:00',NULL,'Y'),
(3,'08-02','5:00','12:00',NULL,'Y'),
(4,'08-03','8:00','5:00',NULL,'Y'),
(5,'08-04','1:00','5:00','Y',NULL),
(6,'08-04','5:00','7:00',NULL,'Y'),
(7,'08-04','7:00','10:00',NULL,'Y'),
(8,'08-04','10:00','13:00',NULL,'Y'),
(9,'08-05','8:00','5:00','Y',NULL)
;WITH CTE AS
(
SELECT
t1.[Date],
t1.TimeIn,
ISNULL(t2.TimeOut, t1.TimeOut) AS TimeOut,
ROW_NUMBER() OVER (PARTITION BY t1.[Date] ORDER BY t1.Id) AS RowNumber
FROM #Test AS t1
LEFT OUTER JOIN #Test AS t2 ON t1.TimeOut = t2.TimeIn AND t1.[Date] = t2.[Date]
)
SELECT
c.[Date],
(SELECT c2.TimeIn FROM CTE AS c2 WHERE c2.[Date] = c.[Date] AND c2.RowNumber = MIN(c.RowNumber)) AS TimeIn,
(SELECT c2.TimeOut FROM CTE AS c2 WHERE c2.[Date] = c.[Date] AND c2.RowNumber = MAX(c.RowNumber)) AS TimeOut
FROM CTE AS c
GROUP BY c.[Date]
You can use merge statements in tables where you have an identical column. You can identify the one or more columns that can be used to uniquely identify the row to be merged.

How can I get this query to return 0 instead of null?

I have this query:
SELECT (SUM(tblTransaction.AmountPaid) - SUM(tblTransaction.AmountCharged)) AS TenantBalance, tblTransaction.TenantID
FROM tblTransaction
GROUP BY tblTransaction.TenantID
But there's a problem with it; there are other TenantID's that don't have transactions and I want to get those too.
For example, the transaction table has 3 rows for bob, 2 row for john and none for jane. I want it to return the sum for bob and john AND return 0 for jane. (or possibly null if there's no other way)
How can I do this?
Tables are like this:
Tenants
ID
Other Data
Transactions
ID
TenantID (fk to Tenants)
Other Data
(You didn't state your sql engine, so I'm going to link to the MySQL documentation).
This is pretty much exactly what the COALESCE() function is meant for. You can feed it a list, and it'll return the first non-null value in the list. You would use this in your query as follows:
SELECT COALESCE((SUM(tr.AmountPaid) - SUM(tr.AmountCharged)), 0) AS TenantBalance, te.ID
FROM tblTenant AS te
LEFT JOIN tblTransaction AS tr ON (tr.TenantID = te.ID)
GROUP BY te.ID;
That way, if the SUM() result would be NULL, it's replaced with zero.
Edited: I rewrote the query using a LEFT JOIN as well as the COALESCE(), I think this is the key of what you were missing originally. If you only select from the Transactions table, there is no way to get information about things not in the table. However, by using a left join from the Tenants table, you should get a row for every existing tenant.
Below is a full walkthrough of the problem. The function isnull has also been included to ensure that a balance of zero (rather than null) is returned for Tenants with no transactions.
create table tblTenant
(
ID int identity(1,1) primary key not null,
Name varchar(100)
);
create table tblTransaction
(
ID int identity(1,1) primary key not null,
tblTenantID int,
AmountPaid money,
AmountCharged money
);
insert into tblTenant(Name)
select 'bob' union all select 'Jane' union all select 'john';
insert into tblTransaction(tblTenantID,AmountPaid, AmountCharged)
select 1,5.00,10.00
union all
select 1,10.00,10.00
union all
select 1,10.00,10.00
union all
select 2,10.00,15.00
union all
select 2,15.00,15.00
select * from tblTenant
select * from tblTransaction
SELECT
tenant.ID,
tenant.Name,
isnull(SUM(Trans.AmountPaid) - SUM(Trans.AmountCharged),0) AS Balance
FROM tblTenant tenant
LEFT JOIN tblTransaction Trans ON
tenant.ID = Trans.tblTenantID
GROUP BY tenant.ID, tenant.Name;
drop table tblTenant;
drop table tblTransaction;
Select Tenants.ID, ISNULL((SUM(tblTransaction.AmountPaid) - SUM(tblTransaction.AmountCharged)), 0) AS TenantBalance
From Tenants
Left Outer Join Transactions Tenants.ID = Transactions.TenantID
Group By Tenents.ID
I didn't syntax check it but it is close enough.
SELECT (SUM(ISNULL(tblTransaction.AmountPaid, 0))
- SUM(ISNULL(tblTransaction.AmountCharged, 0))) AS TenantBalance
, tblTransaction.TenantID
FROM tblTransaction
GROUP BY tblTransaction.TenantID
I only added this because if you're intention is to take into account for one of the parts being null you'll need to do the ISNULL separately
Actually, I found an answer:
SELECT tenant.ID, ISNULL(SUM(trans.AmountPaid) - SUM(trans.AmountCharged),0) AS Balance FROM tblTenant tenant
LEFT JOIN tblTransaction trans
ON tenant.ID = trans.TenantID
GROUP BY tenant.ID

Resources