SQL group by on nested select statement - sql

My data looks like this and here's google spread sheet
I want all the rows selected, along with an added extra row which is life of item (per item_code) in terms of weeks i.e. DATEDIFF(day, min(txn_date), txn_date)/7
I tried something like this:
SELECT txn_date, txn_qty, item_code,
( SELECT DATEDIFF(day, min(txn_date), txn_date)/7
FROM my_table
WHERE item_code like 'X6%'
GROUP BY item_code
) weeks_life
FROM my_table
WHERE item_code like 'X6%'
as for output, it should look like this:

I would use DATEDIFF and a MAX OVER clause for this.
declare #TEMP table (transaction_id int, txn_date date, txn_qty int, code char(5))
INSERT INTO #TEMP VALUES ( 1,'2016-09-20',1,'X6113')
INSERT INTO #TEMP VALUES ( 2,'2016-09-22',4,'X6113')
INSERT INTO #TEMP VALUES ( 3,'2016-11-08',7,'X6117')
INSERT INTO #TEMP VALUES ( 4,'2016-12-10',3,'X6117')
INSERT INTO #TEMP VALUES ( 5,'2016-12-22',1,'X6112')
INSERT INTO #TEMP VALUES ( 6,'2017-01-19',2,'X6118')
INSERT INTO #TEMP VALUES ( 7,'2017-02-11',4,'X6119')
INSERT INTO #TEMP VALUES ( 8,'2016-06-30',1,'X6117')
INSERT INTO #TEMP VALUES ( 9,'2016-08-03',5,'X6110')
INSERT INTO #TEMP VALUES (10,'2016-09-11',7,'X6118')
INSERT INTO #TEMP VALUES (11,'2016-10-29',1,'X6110')
INSERT INTO #TEMP VALUES (12,'2016-11-12',335,'X6113')
INSERT INTO #TEMP VALUES (13,'2017-01-06',1,'X6110')
INSERT INTO #TEMP VALUES (14,'2017-02-06',12,'X6113')
select transaction_id
,txn_date
,DATEDIFF(WEEK,MIN(txn_date) OVER(PARTITION BY code order by code),txn_date) as life_weeks
,txn_qty
,code
from #TEMP
References from MSDN
MAX (Transact-SQL)
OVER Clause (Transact-SQL)
DATEDIFF (Transact-SQL)

You can use window functions. I think this is what you want:
SELECT txn_date, txn_qty, item_code,
DATEDIFF(day,
MIN(txn_date) OVER (PARTITION BY item_code),
txn_date
)/7 as weeks_life
FROM my_table
WHERE item_code like 'X6%';

If you want to use an inline subquery like that, you could do this:
select
txn_date
, txn_qty
, item_code
, ceiling((
datediff(day, (
select min(txn_date)
from t i
where i.item_code = t.item_code
), txn_date)
) / 7.0) as weeks_life
from t
where item_code like 'X6%'
order by
item_code
, txn_date
cross apply() version:
select
txn_date
, txn_qty
, item_code
, ceiling((datediff(day, x.min_txn_date, txn_date)) / 7.0) as weeks_life
from t
cross apply (
select min_txn_date = min(txn_date)
from t i
where i.item_code = t.item_code
) as x
where item_code like 'X6%'
order by
item_code
, txn_date
inner join version:
select
t.txn_date
, t.txn_qty
, t.item_code
, ceiling((datediff(day, x.min_txn_date, t.txn_date)) / 7.0) as weeks_life
from t
inner join (
select min_txn_date = min(txn_date), item_code
from t i
group by item_code
) as x
on x.item_code = t.item_code
where t.item_code like 'X6%'
order by
t.item_code
, t.txn_date
rextester demo: http://rextester.com/BYTL99094
returns:
+------------+---------+-----------+------------+
| txn_date | txn_qty | item_code | weeks_life |
+------------+---------+-----------+------------+
| 2016-08-03 | 5 | X6110 | 0 |
| 2016-10-29 | 1 | X6110 | 13 |
| 2017-01-06 | 1 | X6110 | 23 |
| 2016-12-22 | 1 | X6112 | 0 |
| 2016-09-20 | 1 | X6113 | 0 |
| 2016-09-22 | 4 | X6113 | 1 |
| 2016-11-12 | 335 | X6113 | 8 |
| 2017-02-06 | 12 | X6113 | 20 |
| 2016-06-30 | 1 | X6117 | 0 |
| 2016-11-08 | 7 | X6117 | 19 |
| 2016-12-10 | 3 | X6117 | 24 |
| 2016-09-11 | 7 | X6118 | 0 |
| 2017-01-19 | 2 | X6118 | 19 |
| 2017-02-11 | 4 | X6119 | 0 |
+------------+---------+-----------+------------+

Related

How can I group and sum data by day using T-SQL?

I have a table like this
datex | countx |
---------------------
2022-12-04 | 1 |
2022-12-03 | 2 |
2022-12-02 | 1 |
2022-12-01 | 3 |
2022-11-30 | 1 |
2022-11-29 | 1 |
2022-11-28 | 1 |
2022-11-27 | 2 |
I want to get this output
datex | count_sum |
-------------------------
2022-12 | 4 |
2022-12-01 | 3 |
2022-11 | 5 |
So far I tried some group by clause but I didn't succeed.
Here is test code
declare #test table
(
datex date,
countx int
)
insert into #test
values ('2022-12-04', 1),
('2022-12-03', 2),
('2022-12-02', 1),
('2022-12-01', 3),
('2022-11-30', 1),
('2022-11-29', 1),
('2022-11-28', 1),
('2022-11-27', 2)
You may use a case expression to check if the date is the first day of the month then aggregate as the following:
with check_date as
(
select case
when Day([date])=1
Then Cast([date] as varchar(10))
else Format([date], 'yyyy-MM')
end As dt,
[count]
from table_name
)
select dt, sum([count]) as count_sum
from check_date
group by dt
order by dt desc
See demo
As I understood you want to "extract" year and month from your datex column and count it. I think you can use below SQL:
with cte as(
select
concat(year(datex), '-', month(datex)) as datex,
countx
from test
where not datex in ( '2022-12-01' )
)
select
datex,
count(1)
from cte
group by datex;
Result:
date | count_sum |
-------------------------
2022-12 | 3 |
2022-11 | 4 |
Here is Fiddle.

SQL Server Display total value in separate column where group by already breaks down the total

Sorry for the convoluted title!
I have a table called bookings as follows:
|Booking_id|Client_Id|Supplier_id|
----------------------------------
| 1 | 8 | 123 |
| 2 | 54 | 354 |
| 3 | 54 | 100 |
| 4 | 8 | 123 |
| 5 | 79 | 64 |
| 6 | 123 | 354 |
| 7 | 8 | 354 |
| 8 | 54 | 100 |
| 9 | 123 | 354 |
| 10 | 22 | 123 |
I have to get the % of total bookings per supplier per client
So output should be:
% of total bookings per supplier per client
|Client_id|Supplier_id|NumOfBookings|%_ofClientBookings|
--------------------------------------------------------
| 8 | 123 | 2 | 66 |
| 8 | 354 | 1 | 33 |
| 22 | 123 | 1 | 100 |
| 54 | 354 | 1 | 50 |
| 54 | 100 | 1 | 50 |
| 79 | 64 | 1 | 100 |
| 123 | 354 | 2 | 100 |
The query I have is as follows:
SELECT Client_id, COUNT(Booking_id) As NumOfBookings, Supplier_id,
FROM Booking
GROUP BY Client_id, Supplier_id
ORDER BY Client_id asc, NumOfBookings desc
I need to GROUP_BY Supplier_id and Client_id to see the number of bookings per supplier per client, but then I can't display the % of total bookings per client in the same table because the total booking figure isn't available to me. I can't seem to solve this, any suggestions?
I was able to get the desired result using derived tables, one for the number of bookings by supplier and client, and one for the total bookings by client.
Below is the code I used, you would need to replace #table with your table and ignore the inserts/create table.
IF OBJECT_ID('tempdb..#table') IS NOT NULL
BEGIN
DROP TABLE #table
END
CREATE TABLE #table(Booking_id int, Client_id int, supplier_id int)
insert into #table values(1,8,123)
insert into #table values(2,54,354)
insert into #table values(3,54,100)
insert into #table values(4,8,123)
insert into #table values(5,79,64)
insert into #table values(6,134,354)
insert into #table values(7,8,354)
insert into #table values(8,54,100)
insert into #table values(9,123,354)
insert into #table values(10,22,123)
SELECT NoB.Client_id,NoB.supplier_id,NoB.NumOfBookings, (NoB.NumOfBookings/(NULLIF(Tot.NumOfBookings,0) *1.0)) *100 FROM
(
SELECT Client_id, COUNT(Booking_id) As NumOfBookings, Supplier_id
FROM #table
GROUP BY Client_id, Supplier_id
) NoB JOIN
(
SELECT Client_id, COUNT(Booking_id) As NumOfBookings
FROM #table
GROUP BY Client_id
) Tot on Tot.Client_id=NoB.Client_id
Use "with" or multiple inline views, i.e. queries in from clause. With is simpler.
With bookbyclient as (select that has total for client. Key for result is client)
Now select like you have but join to bookbyclient. In select list divide booking value here by the total from above to get percent
This script will help-
SELECT A.Client_Id,A.Supplier_id,A.C NumOfBookings,
CAST((A.C*1.0)/B.C*100.00 AS INT) AS Percentage
FROM
(
SELECT Client_Id,Supplier_id ,COUNT(Booking_id) C
FROM your_table
GROUP BY Client_Id,Supplier_id
)A
INNER JOIN
(
SELECT Client_Id,COUNT(Booking_id) C
FROM your_table
GROUP BY Client_Id
)B
ON A.Client_Id = B.Client_Id
Try something like this:
SELECT Client_id,
COUNT(Booking_id) As NumOfBookings,
FLOOR(100*(CAST(COUNT(Booking_id) AS FLOAT)/(SELECT COUNT(Booking_id) FROM Booking bb
WHERE bb.Client_id = b.Client_id))),
Supplier_id
FROM Booking b
GROUP BY Client_id, Supplier_id
ORDER BY Client_id asc, NumOfBookings desc
Live Demo.
Try this
SELECT Client_id, NumOfBookings, Supplier_id
, (CAST(NumOfBookings AS DECIMAL(5, 2)) / SUM(NumOfBookings) OVER(PARTITION BY Client_id)) * 100 AS [%_ofClientBookings]
FROM (
SELECT Client_id, COUNT(Booking_id) As NumOfBookings, Supplier_id
FROM Booking
GROUP BY Client_id, Supplier_id
) a
ORDER BY Client_id asc, NumOfBookings desc

SQL: Select top 1 of each group returning multiple records for each group

I have a table which looks like this:
| Id | InvestorFundId | Name | AccountKey | AsOfDate | AddedOn |
| 1 | 11111 | Name1| Key1 | 9/5/2018 | 8/5/2018 |
| 2 | 11111 | Name2| Key1 | 9/3/2018 | 8/5/2018 |
| 3 | 22222 | Name3| Key2 | 9/2/2018 | 8/5/2018 |
| 4 | 33333 | Name4| Key3 | 9/2/2018 | 8/5/2018 |
| 5 | 33333 | Name5| Key3 | 9/4/2018 | 8/5/2018 |
I need to be able to return the most recent InvestorFundId, Name, and AccountKey for each group, based on ordered AsOfDate and AddedOn descending.
Expected result should look like this:
| InvestorFundId | Name | AccountKey |
| 11111 | Name1| Key1 |
| 22222 | Name3| Key2 |
| 33333 | Name5| Key3 |
I've looked at some posts but I can't correctly return the rows, here is what I have so far:
SELECT Name, AccountKey, H.InvestorFundId FROM
[Investor.Fund.History] H
CROSS APPLY(SELECT TOP 1 InvestorFundId
FROM [Investor.Fund.History]
WHERE DataStatusId = 1 AND AsOfYearMonth <= 201806
ORDER BY AsOfDate DESC, AddedOn DESC) HS
ORDER BY H.InvestorFundId
But this is returning me multiple records for each group instead of the most recent.
I just want to add that the current implementation uses ROW_NUMBER but it is too slow, so I am investigating into other solutions.
Thank you
NOTE: I know that I have two extra columns in my where, I just opted to exclude those from the diagram but you should be able to get the gist
You can use a CTE to get the data you need.
USE tempdb;
GO
DECLARE #table TABLE (Id INT IDENTITY(1, 1), InvestorFundId INT, Name VARCHAR(50), AccountKey VARCHAR(50), AsOfDate DATE, AddedOn DATE);
INSERT INTO #table VALUES (11111, 'Name1', 'Key1', '9/5/2018', '8/5/2018');
INSERT INTO #table VALUES (11111, 'Name2', 'Key1', '9/3/2018', '8/5/2018');
INSERT INTO #table VALUES (22222, 'Name3', 'Key2', '9/2/2018', '8/5/2018');
INSERT INTO #table VALUES (33333, 'Name4', 'Key3', '9/2/2018', '8/5/2018');
INSERT INTO #table VALUES (33333, 'Name5', 'Key3', '9/4/2018', '8/5/2018');
;WITH CTE AS
(
SELECT InvestorFundId, Name, AccountKey, ROW_NUMBER() OVER (PARTITION BY InvestorFundID ORDER BY AsOfDate DESC) AS RowId FROM #table
)
SELECT InvestorFundId, Name, AccountKey
FROM CTE
WHERE RowId = 1;
Here is a working SQLFiddle
Hope it helps.
You can use WITH TIES and simply apply ROW_NUMBER in the ORDER BY
Select top 1 with ties *
From History
Where DataStatusId = 1 and AsOfYearMonth = 201806
Order by
Row_Number() over (partition by InvestorFundID order by AsOfDate desc)
Or with a sub query
Select *
From (select * , RN= Row_Number() over (partition by InvestorFundID order by AsOfDate desc)
From History
Where DataStatusId = 1 and AsOfYearMonth = 201806) x
Where x.RN = 1
If you find this slower, then we’d need to see the execution plan to determine WHY it’s slow. A non-clustered index on InvestorFundId, AsOfDate desc would make this super fast.
Create nonclustered index indexName on
History (InvestorFundID, AsOfDate desc)

Customized Pivot in sql 2014

I am trying to do PIVOT on sql, where two columns value has to be aggreagated for each year.
The below code gives perfect result.
DECLARE #TABLE TABLE
(
SKU VARCHAR(10),
YYMM VARCHAR(50),
BRAND VARCHAR(50),
AMT DECIMAL,
QTY INT
)
INSERT INTO #TABLE
SELECT '104591168', '2015-January', 'abott',200, 2 UNION ALL
SELECT '104580709', '2016-January', 'GSK',159 , 2 UNION ALL
SELECT '104720038', '2017-January', 'RANBAXCY',169, 2 UNION ALL
SELECT '10467011A', '2018-January', 'abott',185, 2 UNION ALL
SELECT '104590691', '2019-January', 'abott',256 , 10
SELECT *
FROM(
SELECT BRAND, sku, QTY, YYMM,AMT/QTY AS AVGPR
FROM #TABLE
) AS src
PIVOT(
sum(QTY)
for [YYMM] IN( [2015-January], [2016-January], [2017-January] /* add other moneths here */ )
) AS Pivoted
and result look like
But how can i see AVGPR in same pivot way as like sum(qty).
when i tried like
code:
SELECT *
FROM(
SELECT BRAND, sku, QTY, YYMM
FROM #TABLE
) AS src
PIVOT(
sum(QTY),
SUM(AVG)
for [YYMM] IN( [2015-January], [2016-January], [2017-January] /* add other moneths here */ )
) AS Pivoted
i am getting Incorrect syntax error.
Please help
I have a data like this.
SKU YYMM BRAND Sales Cost QTY AVGPRICE
101110028 1/1/2017 ABOTT 15.7 5.73 1 15.7
101110028 2/1/2017 ABOTT 16.33 5.66 1 16.33
101110028 3/1/2017 ABOTT 31.2 11.34 2 15.6
and
I AM TRYING TO DISPLAY LIKE THIS
Sum of QTY Sum of Avg Price
BRAND PNO 1/1/2017 2/1/2017 3/1/2017 1/1/2017 2/1/2017 3/1/2017
PAGID 101110028 0 2 1 15.7 16.33 15.6
Pivot on Quantity and then sum of avg for a same YYMM In a row
I would just use conditional aggregation:
SELECT brand, sku,
SUM(CASE WHEN YYMM = '2015-January' THEN QTY END) as [2015-January-QTY],
SUM(CASE WHEN YYMM = '2015-January' THEN QTY END) as [2015-January-AVG],
SUM(CASE WHEN YYMM = '2015-February' THEN QTY END) as [2015-February-QTY],
SUM(CASE WHEN YYMM = '2015-February' THEN QTY END) as [2015-February-AVG],
. . .
FROM #TABLE t
GROUP BY brand, sku;
Using two pivots and union all for the result set in your comment
select *, Remarks = 'QTY'
from (select brand, sku, qty, yymm from #table) src
pivot(sum(qty) for [yymm] in (
[2015-January], [2016-January], [2017-January] /* add other months here */
) ) as Pivoted
union all
select *, Remarks = 'AVG'
from (select brand, sku, yymm,amt/qty as avgpr from #table) src
pivot(sum(avgpr) for [yymm] in (
[2015-January], [2016-January], [2017-January] /* add other months here */
) ) as Pivoted
order by brand, sku, remarks desc;
rextester demo: http://rextester.com/ORPF1616
returns:
+----------+-----------+-------------------+------------------+------------------+---------+
| brand | sku | 2015-January | 2016-January | 2017-January | Remarks |
+----------+-----------+-------------------+------------------+------------------+---------+
| abott | 104590691 | NULL | NULL | NULL | QTY |
| abott | 104590691 | NULL | NULL | NULL | AVG |
| abott | 104591168 | 2,0000000000000 | NULL | NULL | QTY |
| abott | 104591168 | 100,0000000000000 | NULL | NULL | AVG |
| abott | 10467011A | NULL | NULL | NULL | QTY |
| abott | 10467011A | NULL | NULL | NULL | AVG |
| gsk | 104580709 | NULL | 2,0000000000000 | NULL | QTY |
| gsk | 104580709 | NULL | 79,5000000000000 | NULL | AVG |
| ranbaxcy | 104720038 | NULL | NULL | 2,0000000000000 | QTY |
| ranbaxcy | 104720038 | NULL | NULL | 84,5000000000000 | AVG |
+----------+-----------+-------------------+------------------+------------------+---------+

How to merge two tables data in one using SQL Server 2008

I have two tables
Table #1: tbl_test1
id | product1 | price1|
---+----------+-------+
1 | A | 200 |
2 | B | 250 |
3 | C | 300 |
Table #2 : tbl_test2
id | product2 | price2|
---+----------+-------+
40 | P | 200 |
20 | Q | 250 |
and I want to result in my given format
id | product1 | price1|id | product2 | price2|
---+----------+-------+---+----------+-------+
1 | A | 200 |50 | P | 200 |
2 | B | 250 |40 | Q | 250 |
3 | C | 300 | | | |
Please help...
I don't know what's your end game but if you just want to display your data side by side you need to use FULL JOIN. Additionally, you have to add a ROW_NUMBER for each of your tables:
WITH CteTest1 AS(
SELECT *,
rn = ROW_NUMBER() OVER(ORDER BY id)
FROM #tbl_test1
),
CteTest2 AS(
SELECT *,
rn = ROW_NUMBER() OVER(ORDER BY id)
FROM #tbl_test2
)
SELECT
t1.id, t1.product1, t1.price1,
t2.id, t2.product2, t2.price2
FROM CteTest1 t1
FULL JOIN CteTest2 t2
ON t2.rn = t1.rn
ONLINE DEMO
declare #FirstTable table (StudentId int, SubjectId int)
declare #SecondTable table (MarksId int, RankId int, LastRank int)
insert into #FirstTable values (1, 1)
insert into #FirstTable values (1, 2)
insert into #FirstTable values (1, 3)
insert into #SecondTable values (1, 4, 10)
insert into #SecondTable values (1, 5, 11)
;with XmlTable (RowNumber, StudentId, SubjectId) as
(
select row_number() over(order by StudentId) as RowNumber, * from #FirstTable
)
,TechnicalIdsTable (RowNumber, MarksId, RankId, LastRank) as
(
select row_number() over(order by MarksId) as RowNumber, * from #SecondTable
)
select x.StudentId, x.SubjectId, t.MarksId, t.RankId, t.LastRank from XmlTable x
left join TechnicalIdsTable t
on x.RowNumber = t.RowNumber