How to Combine Three table with aggregate functions in Sql Server - 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]

Related

SQL - Attain Previous Transaction Informaiton [duplicate]

I need to calculate the difference of a column between two lines of a table. Is there any way I can do this directly in SQL? I'm using Microsoft SQL Server 2008.
I'm looking for something like this:
SELECT value - (previous.value) FROM table
Imagining that the "previous" variable reference the latest selected row. Of course with a select like that I will end up with n-1 rows selected in a table with n rows, that's not a probably, actually is exactly what I need.
Is that possible in some way?
Use the lag function:
SELECT value - lag(value) OVER (ORDER BY Id) FROM table
Sequences used for Ids can skip values, so Id-1 does not always work.
SQL has no built in notion of order, so you need to order by some column for this to be meaningful. Something like this:
select t1.value - t2.value from table t1, table t2
where t1.primaryKey = t2.primaryKey - 1
If you know how to order things but not how to get the previous value given the current one (EG, you want to order alphabetically) then I don't know of a way to do that in standard SQL, but most SQL implementations will have extensions to do it.
Here is a way for SQL server that works if you can order rows such that each one is distinct:
select rank() OVER (ORDER BY id) as 'Rank', value into temp1 from t
select t1.value - t2.value from temp1 t1, temp1 t2
where t1.Rank = t2.Rank - 1
drop table temp1
If you need to break ties, you can add as many columns as necessary to the ORDER BY.
WITH CTE AS (
SELECT
rownum = ROW_NUMBER() OVER (ORDER BY columns_to_order_by),
value
FROM table
)
SELECT
curr.value - prev.value
FROM CTE cur
INNER JOIN CTE prev on prev.rownum = cur.rownum - 1
Oracle, PostgreSQL, SQL Server and many more RDBMS engines have analytic functions called LAG and LEAD that do this very thing.
In SQL Server prior to 2012 you'd need to do the following:
SELECT value - (
SELECT TOP 1 value
FROM mytable m2
WHERE m2.col1 < m1.col1 OR (m2.col1 = m1.col1 AND m2.pk < m1.pk)
ORDER BY
col1, pk
)
FROM mytable m1
ORDER BY
col1, pk
, where COL1 is the column you are ordering by.
Having an index on (COL1, PK) will greatly improve this query.
LEFT JOIN the table to itself, with the join condition worked out so the row matched in the joined version of the table is one row previous, for your particular definition of "previous".
Update: At first I was thinking you would want to keep all rows, with NULLs for the condition where there was no previous row. Reading it again you just want that rows culled, so you should an inner join rather than a left join.
Update:
Newer versions of Sql Server also have the LAG and LEAD Windowing functions that can be used for this, too.
select t2.col from (
select col,MAX(ID) id from
(
select ROW_NUMBER() over(PARTITION by col order by col) id ,col from testtab t1) as t1
group by col) as t2
The selected answer will only work if there are no gaps in the sequence. However if you are using an autogenerated id, there are likely to be gaps in the sequence due to inserts that were rolled back.
This method should work if you have gaps
declare #temp (value int, primaryKey int, tempid int identity)
insert value, primarykey from mytable order by primarykey
select t1.value - t2.value from #temp t1
join #temp t2
on t1.tempid = t2.tempid - 1
Another way to refer to the previous row in an SQL query is to use a recursive common table expression (CTE):
CREATE TABLE t (counter INTEGER);
INSERT INTO t VALUES (1),(2),(3),(4),(5);
WITH cte(counter, previous, difference) AS (
-- Anchor query
SELECT MIN(counter), 0, MIN(counter)
FROM t
UNION ALL
-- Recursive query
SELECT t.counter, cte.counter, t.counter - cte.counter
FROM t JOIN cte ON cte.counter = t.counter - 1
)
SELECT counter, previous, difference
FROM cte
ORDER BY counter;
Result:
counter
previous
difference
1
0
1
2
1
1
3
2
1
4
3
1
5
4
1
The anchor query generates the first row of the common table expression cte where it sets cte.counter to column t.counter in the first row of table t, cte.previous to 0, and cte.difference to the first row of t.counter.
The recursive query joins each row of common table expression cte to the previous row of table t. In the recursive query, cte.counter refers to t.counter in each row of table t, cte.previous refers to cte.counter in the previous row of cte, and t.counter - cte.counter refers to the difference between these two columns.
Note that a recursive CTE is more flexible than the LAG and LEAD functions because a row can refer to any arbitrary result of a previous row. (A recursive function or process is one where the input of the process is the output of the previous iteration of that process, except the first input which is a constant.)
I tested this query at SQLite Online.
You can use the following funtion to get current row value and previous row value:
SELECT value,
min(value) over (order by id rows between 1 preceding and 1
preceding) as value_prev
FROM table
Then you can just select value - value_prev from that select and get your answer

SQL Server: COUNT used with WHERE

so I'd consider myself really new to SQL Server so the less used keywords like HAVING and COUNT() etc. So when I got this error:
An aggregate may not appear in the WHERE clause unless it is in a
subquery contained in a HAVING clause or a select list, and the column
being aggregated is an outer reference.
I was really confused by the last bit. "a select list?" "column being aggregated is an outer reference?" Can anyone explain this in layman's terms?
It's basically saying you need to use a subquery that references another table if you want to use aggregates in those places:
SELECT A,
B,
C
FROM Table T
WHERE A = (SELECT MAX(D) FROM Table T2 WHERE T2.A = T.A)
--Valid, MAX(D) is an outer reference to another table we call T2
SELECT A,
B,
C
FROM Table T
WHERE A = MAX(D) --Invalid
The HAVING version would be something like this:
SELECT A,
B,
C
FROM Table T
GROUP BY A,
B,
C
HAVING COUNT(*) > (SELECT MAX(D) FROM Table T2) --Valid
SELECT A,
B,
C
FROM Table T
GROUP BY A,
B,
C
HAVING COUNT(*) > MAX(D) --Invalid
The SELECT-list is
SELECT a, b, c ... <=== this list of expressions after SELECT
An outer reference is a column of the surrounding query referenced in a subquery. This is clearly explained here: Aggregates with an Outer Reference
Note that the WHERE-clause is applied before grouping (with GROUP BY) and the HAVING-clause after grouping. Therefore the aggregate functions can appear in the HAVING-clause but not in the WHERE clause.
SELECT customer_id, COUNT(*) as number_of_orders, SUM(amount) AS total_amount
FROM cust_orders
WHERE year(order_date) = 2017 -- filters records before grouping.
GROUP BY customer_id -- groups while counting and summing up.
HAVING COUNT(*) > 2 -- count is available here.
This selects all the customer orders of the year 2017 and calculates the totals per customer. Only customers having more than 2 orders in this year are returned.
Basically what it says is that you cannot do this:
WHERE COUNT(ColumnA) = 100
You need a HAVING after the GROUP BY:
SELECT COUNT(ColumnA) AS CountA, ColumnB, ColumnC
FROM Table
GROUP BY ColumnB, ColumnC
HAVING COUNT(ColumnA) = 100

SQL left join table with double sum record

I have two tables (dw and click) and using left join to join it if match the date.
both tables have date 2016-06-01 (dw table has 1 record and click table has 2 records). I want to sum the column winloss in dw table, but since click table has 2 record with the date 2016-06-01 and caused the winloss double sum. But i just want sum the winloss in dw table. Any suggestion? Please refer to image below.
You can do this with a temp table or a common table expression. Here is with a temp.
If object_id('tempdb..#temp') is not null drop table #temp
select
sum(winloss) as winloss,
updated
--add any other columns you want
into #temp
group by
updated
--add other columns to group
select
t.updated,
t.winloss,
c.*
from #temp t
left join click on c.trandate = t.updated
Try this:
SELECT *
FROM DW D
INNER JOIN CLICK C
ON C.CLICKED = D.CLICKCOUNT
AND C.TRANDATE = D.UPDATED
Try this
SELECT dw.[updated]
,sum(distinct [winloss])
from dw,Click
where dw.updated = Click.trandate
FROM dw, Click
group by dw.updated

left join to get sum of column in second table

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.

SELECT from multiple queries

I have this tables:
tblDiving(
diving_number int primary key
diving_club int
date_of_diving date)
tblDivingClub(
number int primary key not null check (number>0),
name char(30),
country char(30))
tblWorks_for(
diver_number int
club_number int
end_working_date date)
tblCountry(
name char(30) not null primary key)
I need to write a query to return a name of a country and the number of "Super club" in it.
a Super club is a club which have more than 25 working divers (tblWorks_for.end_working_date is null) or had more than 100 diving's in it(tblDiving) in the last year.
after I get the country and number of super club, I need to show only the country's that contains more than 2 super club.
I wrote this 2 queries:
select tblDivingClub.name,count(distinct tblWorks_for.diver_number) as number_of_guids
from tblWorks_for
inner join tblDivingClub on tblDivingClub.number = tblWorks_for.club_number,tblDiving
where tblWorks_for.end_working_date is null
group by tblDivingClub.name
select tblDivingClub.name, count(distinct tblDiving.diving_number) as number_of_divings
from tblDivingClub
inner join tblDiving on tblDivingClub.number = tblDiving.diving_club
WHERE tblDiving.date_of_diving <= DATEADD(year,-1, GETDATE())
group by tblDivingClub.name
But I don't know how do I continue.
Every query works separately, but how do I combine them and select from them?
It's university assignment and I'm not allowed to use views or temporary tables.
It's my first program so I'm not really sure what I'm doing:)
WITH CTE AS (
select tblDivingClub.name,count(distinct tblWorks_for.diver_number) as diving_number
from tblWorks_for
inner join tblDivingClub on tblDivingClub.number = tblWorks_for.club_number,tblDiving
where tblWorks_for.end_working_date is null
group by tblDivingClub.name
UNION ALL
select tblDivingClub.name, count(distinct tblDiving.diving_number) as diving_number
from tblDivingClub
inner join tblDiving on tblDivingClub.number = tblDiving.diving_club
WHERE tblDiving.date_of_diving <= DATEADD(year,-1, GETDATE())
group by tblDivingClub.name
)
SELECT * FROM CTE
You can combine the queries using a UNION ALL as long as there are the same number of columns in each query. You can then roll them into a Common Table Expression (CTE) and do a select from that.

Resources