SQL SUM Values need and then AVG of 2 Tables - sql-server

From this it is possible for the following :
TABLE 1
Id | final | Date
------------------
1 236 02-11-14
2 10 07-01-12
3 58 09-02-10
TABLE 2
Id | final | Date
------------------
1 330 02-11-14
2 5 07-01-12
3 100 09-02-10
ADD both Table 1 and Table 2 Sum'd values(column final), and then work out the AVG number from this and create this as another column average, THEN if table2 for example SUM'd original amount (before the AVG) is higher than Table 1 SUM'd amount create another column and print in that column 'Tbl2 has the higher amount' and vise verser if table 1 had the higher amount.
End result Column wise table would look like this :
|tb1_final_amount|tb2_final_amount|Avg_Amount|Top_Score_tbl
|tb1_final_amount|tb2_final_amount|Avg_Amount|Top_Score_tbl
304 435 369.5 tb2 has highest score

This is one way (of many) to do this. You can sum up the two tables and use them as derived tables in a query like so:
select
tb1_final_amount,
tb2_final_amount,
(tb1_final_amount+tb2_final_amount)/2.0 as Avg_Amount,
case
when tb1_final_amount < tb2_final_amount then 'tb2 has highest score'
else 'tb1 has highest score'
end as Top_Score_tbl
from
(select SUM(final) as tb1_final_amount from TABLE1) t1,
(select SUM(final) as tb2_final_amount from TABLE2) t2

This does the trick!:
--SET UP Table1
CREATE TABLE Table1 (ID INT, final INT, [Date] DATETIME)
INSERT Table1 VALUES (1, 236, '20141102')
INSERT Table1 VALUES (2, 10, '20120107')
INSERT Table1 VALUES (3, 58, '20100209')
--SET UP Table2
CREATE TABLE Table2 (ID INT, final INT, [Date] DATETIME)
INSERT Table2 VALUES (1, 330, '20141102')
INSERT Table2 VALUES (2, 5, '20120107')
INSERT Table2 VALUES (3, 100, '20100209')
-- Query
SELECT
SUM(CASE WHEN t.TableName = 'Table1' THEN T.final
ELSE 0
END) AS tb1_final_amount,
SUM(CASE WHEN t.TableName = 'Table2' THEN T.final
ELSE 0
END) AS tb2_final_amount,
AVG(T.final) AS Avg_Amount,
ISNULL((
SELECT
'Table1'
FROM
Table1 T1
WHERE
SUM(CASE WHEN t.TableName = 'Table1' THEN T.final
ELSE 0
END) > SUM(CASE WHEN t.TableName = 'Table2' THEN T.final
ELSE 0
END)
), 'Table2')
FROM
(
SELECT
'Table1' AS TableName,
final
FROM
Table1
UNION ALL
SELECT
'Table2',
final
FROM
Table2
) AS T

Related

SQL Server: max of date

Table 1
RefId Name
----- ----
1 A
2 B
Table 2
RefId Date
----- -----
1 29/03/2018 07:15
1 29/03/2018 07:30
2 29/03/2018 07:35
2 29/03/2018 07:40
I would like the result to be as follows (Refid name and the max(date) from table 1 and 2 for that refid)
1 A 29/03/2018 07:30
2 B 29/03/2018 07:40
Query used
select
table1.refId, table1.name,
(select max(date) from table2)
from
table1, table2
where
table1.refid = table2.refid
group by
table2.refid
I am getting the following error message
Column is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
Use JOIN and the aggregate function MAX with GROUP BY to select the max date for each RefId.
Query
select [t1].[RefId], [t1].[Name], max([t2].[date] as [date]
from [Table1] [t1]
join [Table2] [t2]
on [t1].[RefId] = [t2].[RefId]
group by [t1].[RefId], [t1].[Name];
'29/03/2018 07:15' is nvarchar-type, you need datetime.
nvarchar convert to datetime: SELECT CONVERT(datetime, '29/03/2018 07:15', 103)
Answer to your example:
DECLARE #Table1 TABLE(RefId int, Name nvarchar(10));
INSERT INTO #Table1(RefId, Name) VALUES(1, 'A'), (2, 'B');
DECLARE #Table2 TABLE(RefId int, [Date] nvarchar(50));
INSERT INTO #Table2(RefId, [Date])
VALUES
(1, '29/03/2018 07:15'),
(1, '29/03/2018 07:30'),
(2, '29/03/2018 07:35'),
(2, '29/03/2018 07:40');
SELECT t1.RefId, t1.Name, t2.Date
FROM #Table1 AS t1
INNER JOIN
(SELECT RefId, MAX(CONVERT(datetime, [Date], 103)) AS [Date]
FROM #Table2
GROUP BY RefId) AS t2
ON t1.RefId = t2.RefId

Group Function based on two different Values

I am having one order table which is containing the orders.
Each order is having specific cost related to it and also status of the order.
So i need 4 columns in the output.
1st will show count of the records having status id as 1 & 2.
2nd will show sum of cost of the records having status id as 1 & 2
3rd will show count of the records having status id not in 1 & 2.
4th will show sum of cost of the records having status id not in 1 & 2.
I am not getting the approach how to fix such issue so that i don't need to hit the database many times.Any solution
If I understand correctly, I have a simple example for your case.
You could use SUM and CASE WHEN to achieve it.
Hope it helps.
DECLARE #SampleData AS TABLE
(
Id int IDENTITY (1,1),
StatusId int,
Cost decimal(10,2)
)
INSERT INTO #SampleData
(
--Id - this column value is auto-generated
StatusId,
Cost
)
VALUES
(1, 10),(2, 10),(3, 20), (4, 20),
(1, 10),(2, 10),(3, 20), (4, 20),
(1, 10),(2, 10),(3, 20), (4, 20)
SELECT
sum(CASE WHEN sd.StatusId IN (1,2) THEN 1 ELSE 0 END) AS TotalCount_1_2,
sum(CASE WHEN sd.StatusId IN (1,2) THEN sd.Cost ELSE 0 END) AS TotalCost_1_2,
sum(CASE WHEN sd.StatusId NOT IN (1,2) THEN 1 ELSE 0 END) AS TotalCount_Not_in_1_2,
sum(CASE WHEN sd.StatusId NOT IN (1,2) THEN sd.Cost ELSE 0 END) TotalCost_Not_in_1_2
FROM #SampleData sd
Returns
TotalCount_1_2 TotalCost_1_2 TotalCount_Not_in_1_2 TotalCost_Not_in_1_2
----------------------------------------------------------------------------
6 60.00 6 120.00
Do you want this.
SELECT COUNT(f1) AS Col1
,SUM(f1) AS Col2
,(SELECT COUNT(f1) FROM yourTable t2 WHERE t1.id1 <> t2.id1 OR t1.id2 <> t2.id2) AS Col3
,(SELECT SUM(f1) FROM yourTable t3 WHERE t1.id1 <> t3.id1 OR t1.id2 <> t3.id2) AS Col4
FROM yourTable t1
GROUP BY id1,id2

SQL - Filter on dates X number of days apart from the previous

I have a table containing orders. I would like to select those orders that are a certain number of days apart for a specific client. For example, in the table below I would like to select all of the orders for CustomerID = 10 that are at least 30 days apart from the previous instance. With the starting point to be the first occurrence (07/05/2014 in this data).
OrderID | CustomerID | OrderDate
==========================================
1 10 07/05/2014
2 10 07/15/2014
3 11 07/20/2014
4 11 08/20/2014
5 11 09/21/2014
6 10 09/23/2014
7 10 10/15/2014
8 10 10/30/2014
I would want to select OrderIDs (1,6,8) since they are 30 days apart from each other and all from CustomerID = 10. OrderIDs 2 and 7 would not be included as they are within 30 days of the previous order for that customer.
What confuses me is how to set the "checkpoint" to the last valid date. Here is a little "pseudo" SQL.
SELECT OrderID
FROM Orders
WHERE CusomerID = 10
AND OrderDate > LastValidOrderDate + 30
i came here and i saw #SveinFidjestøl already posted answer but i can't control my self after by long tried :
with the help of LAG and LEAD we can comparison between same column
and as per your Q you are looking 1,6,8. might be this is helpful
SQL SERVER 2012 and after
declare #temp table
(orderid int,
customerid int,
orderDate date
);
insert into #temp values (1, 10, '07/05/2014')
insert into #temp values (2, 10, '07/15/2014')
insert into #temp values (3, 11, '07/20/2014')
insert into #temp values (4, 11, '08/20/2014')
insert into #temp values (5, 11, '09/21/2014')
insert into #temp values (6, 10, '09/23/2014')
insert into #temp values (7, 10, '10/15/2014')
insert into #temp values (8, 10, '10/30/2014');
with cte as
(SELECT orderid,customerid,orderDate,
LAG(orderDate) OVER (ORDER BY orderid ) PreviousValue,
LEAD(orderDate) OVER (ORDER BY orderid) NextValue,
rownum = ROW_NUMBER() OVER (ORDER BY orderid)
FROM #temp
WHERE customerid = 10)
select orderid,customerid,orderDate from cte
where DATEDIFF ( day , PreviousValue , orderDate) > 30
or PreviousValue is null or NextValue is null
SQL SERVER 2005 and after
WITH CTE AS (
SELECT
rownum = ROW_NUMBER() OVER (ORDER BY p.orderid),
p.orderid,
p.customerid,
p.orderDate
FROM #temp p
where p.customerid = 10)
SELECT CTE.orderid,CTE.customerid,CTE.orderDate,
prev.orderDate PreviousValue,
nex.orderDate NextValue
FROM CTE
LEFT JOIN CTE prev ON prev.rownum = CTE.rownum - 1
LEFT JOIN CTE nex ON nex.rownum = CTE.rownum + 1
where CTE.customerid = 10
and
DATEDIFF ( day , prev.orderDate , CTE.orderDate) > 30
or prev.orderDate is null or nex.orderDate is null
GO
You can use the LAG() function, available in SQL Server 2012, together with a Common Table Expression. You calculate the days between the customer's current order and the customer's previous order and then query the Common Table Expression using the filter >= 30
with cte as
(select OrderId
,CustomerId
,datediff(d
,lag(orderdate) over (partition by CustomerId order by OrderDate)
,OrderDate) DaysSinceLastOrder
from Orders)
select OrderId, CustomerId, DaysSinceLastOrder
from cte
where DaysSinceLastOrder >= 30 or DaysSinceLastOrder is null
Results:
OrderId CustomerId DaysSinceLastOrder
1 10 NULL
6 10 70
3 11 NULL
4 11 31
5 11 32
(Note that 1970-01-01 is chosen arbitrarily, you may choose any date)
Update
A slighty more reliable way of doing it will involve a temporary table. But the original table tbl can be left unchanged. See here:
CREATE TABLE #tmp (id int); -- set-up temp table
INSERT INTO #tmp VALUES (1); -- plant "seed": first oid
WHILE (##ROWCOUNT>0)
INSERT INTO #tmp (id)
SELECT TOP 1 OrderId FROM tbl
WHERE OrderId>0 AND CustomerId=10
AND OrderDate>(SELECT max(OrderDate)+30 FROM tbl INNER JOIN #tmp ON id=OrderId)
ORDER BY OrderDate;
-- now list all found entries of tbl:
SELECT * FROM tbl WHERE EXISTS (SELECT 1 FROM #tmp WHERE id=OrderId)
#tinka shows how to use CTEs to do the trick, and the new windowed functions (for 2012 and later) are probably the best answer. There is also the option, assuming you do not have a very large data set, to use a recursive CTE.
Example:
declare #customerid int = 10;
declare #temp table
(orderid int,
customerid int,
orderDate date
);
insert into #temp values (1, 10, '07/05/2014')
insert into #temp values (2, 10, '07/15/2014')
insert into #temp values (3, 11, '07/20/2014')
insert into #temp values (4, 11, '08/20/2014')
insert into #temp values (5, 11, '09/21/2014')
insert into #temp values (6, 10, '09/23/2014')
insert into #temp values (7, 10, '10/15/2014')
insert into #temp values (8, 10, '10/30/2014');
with datefilter AS
(
SELECT row_number() OVER(PARTITION BY CustomerId ORDER BY OrderDate) as RowId,
OrderId,
CustomerId,
OrderDate,
DATEADD(day, 30, OrderDate) as FilterDate
from #temp
WHERE CustomerId = #customerid
)
, firstdate as
(
SELECT RowId, OrderId, CustomerId, OrderDate, FilterDate
FROM datefilter
WHERE rowId = 1
union all
SELECT datefilter.RowId, datefilter.OrderId, datefilter.CustomerId,
datefilter.OrderDate, datefilter.FilterDate
FROM datefilter
join firstdate
on datefilter.CustomerId = firstdate.CustomerId
and datefilter.OrderDate > firstdate.FilterDate
WHERE NOT EXISTS
(
SELECT 1 FROM datefilter betweens
WHERE betweens.CustomerId = firstdate.CustomerId
AND betweens.orderdate > firstdate.FilterDate
AND datefilter.orderdate > betweens.orderdate
)
)
SELECT * FROM firstdate

Find a persons closing ratio

I have staff members that are assigned tasks, I need to find the percentage of tasks that a staff member has completed year-to-date... of those that were assigned to him. If John is assigned 10 tasks, and completed 5 I need to show John has closed .50 (50%).
I have two tables:
Tasks and Tasks_cstm
Tasks t
| ID | STATUS |Date_Completed|
The statuses are 'In Progress', 'Not Started', 'Completed'
Tasks_cst tc
| ID_C|Staff_Member|
The tables are joined on t.id = tc.id_c
This returns the number completed:
(select count(*)as Completed from tasks_CSTM tc
join tasks t
on t.id = tc.id_c
where status = 'completed'
group by staff_member_C )
This returns the total number of tasks:
(select count(*)as Total from tasks_CSTM tc
join tasks t
on t.id = tc.id_c
group by staff_member_C )
This is what I've come up with, but it errors: Subquery returned more than 1 value.
select staff_member_c,((select count(*)as Completed from tasks_CSTM tc
join tasks t
on t.id = tc.id_c
where status = 'completed'
group by staff_member_C )/(select count(*)as Total from tasks_CSTM tc
join tasks t
on t.id = tc.id_c
group by staff_member_C ))
from tasks t
join tasks_CSTM tc
on t.id = tc.id_C
group by staff_member_C
Any help is appreciated.
Something like this I think:
select staff_member_c, sum(case when status='completed' then 1.0 end)/count(*) as pctCompleted
from tasks_cstm tc
join tasks t
on t.id = tc.id_c
group by staff_member_c
You might need "else 0.0" in the case statement (but don't in MSSQL), and you might need nullif(count(*),0) in the denominator (but probably not in any DBMS).
There's a couple issues here to grapple with. One of which is dealing with the "year-to-date" part. Right now, with a Date_Completed column, there's no way to know when a task was assigned/created, which invalidates our ability to know year-to-date info. Barring that part of the question, here's my query which should work. I have a WHERE clause commented out which can easily be adapted to use a Date_Assigned column for year-to-date info.
select
staff_member
, sum(case t.status when 'Completed' then 1.0 else 0 end) [Completed]
, count(*) [Total]
, sum(case t.status when 'Completed' then 1.0 else 0 end) / count(*) [CompletedPercent]
from
tasks t
inner join tasks_cstm tc
on t.id = tc.id_C
--where
-- dateadd(year, datediff(year, 0, Date_Assigned), 0) = dateadd(year, datediff(year, 0, getdate()), 0)
group by
staff_member
And here's the setup code I used to (un-comprehensibly) test it out:
create table tasks (ID int, Status varchar(50), Date_Completed date)
create table tasks_cstm (ID_C int, Staff_Member varchar(50))
insert into tasks
select 1, 'Not Started', null
union all
select 2, 'Completed', '2011-04-15'
union all
select 3, 'In Progress', null
insert into tasks_cstm
select 1, 'Cadaeic'
union all
select 2, 'Cadaeic'
union all
select 3, 'Cadaeic'
Resulting in this:
staff_member Completed Total CompletedPercent
------------------- -------------------- ----------- -----------------------
Cadaeic 1.0 3 0.333333
-- Tasks
declare #T table(ID int, Status varchar(20))
-- Tasks_cst
declare #TC table(ID_C int, Staff_Member varchar(20))
insert into #TC values (1, 'Staff 1')
insert into #TC values (2, 'Staff 2')
insert into #TC values (3, 'Staff 3')
insert into #T values (1, 'Completed')
insert into #T values (1, 'Completed')
insert into #T values (1, 'In Progress')
insert into #T values (2, 'Completed')
insert into #T values (2, 'In Progress')
insert into #T values (3, 'In Progress')
select *
from #TC as TC
cross apply
(select sum(case T.Status when 'Completed' then 1.0 else 0.0 end) / count(*)
from #T as T
where T.ID = TC.ID_C) as C(PrecentCompleted)
Result
ID_C Staff_Member PrecentCompleted
----------- -------------------- ---------------------------------------
1 Staff 1 0.666666
2 Staff 2 0.500000
3 Staff 3 0.000000

Convert rows to columns

I have the follwoing structure:
Emp PayDate Amount
1 11/23/2010 500
1 11/25/2010 -900
1 11/28/2010 1000
1 11/29/2010 2000
2 11/25/2010 2000
3 11/28/2010 -3000
2 11/28/2010 4000
3 11/29/2010 -5000
I need to get the following result if emp 1 is selected (top 3 dates and their corresponding vals - if they exist - 4th row is always ignored)
PayDate1 Amount1 Paydate2 Amount2 Paydate3 Amount3
11/23/2010 500 11/25/2010 -900 11/28/2010 1000
I need to get the following result if emp 2 is selected
Paydate1 Amount1 Paydate2 Amount2 Paydate3 Amount3
11/25/2010 2000 11/28/2010 4000 NULL NULL
I need to get the following result if emp 3 is selected
Paydate1 Amount1 Paydate2 Amount2 Paydate3 Amount3
11/28/2010 -3000 11/29/2010 -5000
To get the respective data in rows I can run the following query:
select top 3 Paydate, Amount from Table where Emp = #Emp
But how do I get result in a pivoted fashion?
There's an excellent article on Pivots with SQL Server 2005+ here.
CREATE TABLE dbo.Table1
(
Emp int,
PayDate datetime,
Amount int
)
GO
INSERT INTO dbo.Table1 VALUES (1, '11/23/2010',500)
INSERT INTO dbo.Table1 VALUES (1, '11/25/2010',-900)
INSERT INTO dbo.Table1 VALUES (1, '11/28/2010',1000)
INSERT INTO dbo.Table1 VALUES (1, '11/29/2010',2000)
INSERT INTO dbo.Table1 VALUES (2, '11/25/2010',2000)
INSERT INTO dbo.Table1 VALUES (3, '11/28/2010',-3000)
INSERT INTO dbo.Table1 VALUES (2, '11/28/2010',4000)
INSERT INTO dbo.Table1 VALUES (3, '11/29/2010',-5000)
;WITH cte AS
(SELECT Emp, PayDate, Amount, PayDateRowNumber
FROM
(SELECT Emp,
PayDate,
Amount,
ROW_NUMBER() OVER (PARTITION BY Emp ORDER BY PayDate) AS PayDateRowNumber
FROM Table1) AS RankedTable1
WHERE PayDateRowNumber < 4)
SELECT c1.Emp AS Emp, c1.PayDate AS PayDate1
,c1.Amount AS Amount1, c2.PayDate AS PayDate2
,c2.Amount AS Amount2, c3.PayDate AS PayDate3, c3.Amount AS Amount3
FROM cte c1
LEFT JOIN cte c2 ON c2.Emp = c1.Emp AND c2.PayDateRowNumber = 2
LEFT JOIN cte c3 ON c3.Emp = c2.Emp AND c3.PayDateRowNumber = 3
WHERE c1.PayDateRowNumber = 1
Output is:
Some caveats are that it won't aggregate amounts for the same employer/date (though can easily be changed). Also may want to change to review use of ROW_NUMBER() versus RANK() and DENSE_RANK() depending on your definition of "TOP 3"

Resources