Aggregating different rows in one column SQL Query - sql

Sorry, I didn't come up with a good title for the question, so feel free to change it accordingly.
I may describe my question with a minimal example in MS SQL server 2012:
create table #tmp
(
RowID varchar(10),
SectionCode int,
SectionName varchar(10)
)
insert into #tmp values('Record1' , 1 , 'AB');
insert into #tmp values('Record1' , 2 , 'CD');
insert into #tmp values('Record1' , 3 , 'EF');
insert into #tmp values('Record2' , 1 , 'AB');
insert into #tmp values('Record2' , 4 , 'GH');
insert into #tmp values('Record2' , 5 , 'IJ');
insert into #tmp values('Record3' , 2 , 'CD');
insert into #tmp values('Record3' , 5 , 'IJ');
I am trying to create a one row per record result in which every section is a column and if there is a row associated with a section, the corresponding column value is increased. This is (not) what I want (the same record data on different rows)
select RowID,
case when SectionName = 'AB' then 1 else 0 end as [AB Section] ,
case when SectionName = 'CD' then 1 else 0 end as [CD Section] ,
case when SectionName = 'EF' then 1 else 0 end as [EF Section] ,
case when SectionName = 'GH' then 1 else 0 end as [GH Section] ,
case when SectionName = 'IJ' then 1 else 0 end as [IJ Section]
from #tmp
group by RowID , SectionName
which gives this output:
I need this:
Thanks in advance

You can use pivot for this as below and manipulate the values of sections however you want.
SELECT rowid
,CASE
WHEN ab IS NULL
THEN 0
ELSE 1
END AS ab
,CASE
WHEN cd IS NULL
THEN 0
ELSE 1
END AS cd
,CASE
WHEN ef IS NULL
THEN 0
ELSE 1
END AS ef
,CASE
WHEN gh IS NULL
THEN 0
ELSE 1
END AS gh
,CASE
WHEN ij IS NULL
THEN 0
ELSE 1
END AS ij
FROM (
SELECT *
FROM #tmp
PIVOT(MAX(Sectioncode) FOR Sectionname IN (
AB
,CD
,EF
,GH
,IJ
)) pvt
) tab
I think the result you shown is not correct for record id 2. ij of record id 2 should be 1.

I think you want this:
select RowID,
sum(case when SectionName = 'AB' then 1 else 0 end) as [AB Section] ,
sum(case when SectionName = 'CD' then 1 else 0 end) as [CD Section] ,
sum(case when SectionName = 'EF' then 1 else 0 end) as [EF Section] ,
sum(case when SectionName = 'GH' then 1 else 0 end) as [GH Section] ,
from #tmp
group by RowID;
That is, you need aggregation functions. And the group by should contain the columns that you want to define each row (i.e. only the RowId).

Related

calculate SUM of multiple Case When

My data has 3 groups of variables (set1_a,b,c,) (set2_x,y), (set3_n) as below. For each group, if at least 1 variable has value>90 then I count as 1.
Then, I SUM the count.
My code below works fine. However, I would like to put all in 1 select statement.
Can you please help?
Create TABLE have (
id varchar(225),
set1_a varchar(225),
set1_b varchar(225),
set1_c varchar(225),
set2_x varchar(225),
set2_y varchar(225),
set3_n varchar(225)
);
Insert into have (id,set1_a,set1_b,set1_c,set2_x,set2_y,set3_n) values (1,1,3,200,1,1,5);
Insert into have (id,set1_a,set1_b,set1_c,set2_x,set2_y,set3_n) values (2,1,3,200,200,1,5);
Insert into have (id,set1_a,set1_b,set1_c,set2_x,set2_y,set3_n) values (3,1,3,200,200,1,500);
Insert into have (id,set1_a,set1_b,set1_c,set2_x,set2_y,set3_n) values (4,1,3,1,1,1,500);
select * from have;
SELECT id,set1_a,set1_b,set1_c,set2_x,set2_y,set3_n,N1,N2, count1+count2+count3 as total_count FROM
(
select id,set1_a,set1_b,set1_c,set2_x,set2_y,set3_n,
case
when (set1_a >90 or set1_b>90 or set1_c>90) then 1 else 0
end as count1,
case
when (set2_x >90 or set2_y>90) then 1 else 0
end as count2
case
when (set3_n >90) then 1 else 0
end as count3
from have
)
--WHERE N1+N2>=2
;
This works. Also, ideally the data type should be int and not varchar since you are comparing with int.
SELECT id,set1_a,set1_b,set1_c,set2_x,set2_y,set3_n,
(case
when greatest(
cast(set1_a as int),
cast(set1_b as int),
cast(set1_c as int)
)>90 then 1 else 0 end) +
(case
when greatest(
cast(set2_x as int),
cast(set2_y as int)
)>90 then 1 else 0 end) +
(case
when cast(set3_n as int) > 90
then 1 else 0 end
) as total_count
from have
DB fiddle on postgres 11 for the same. But it's all standard sql.

Select ranges from tables of ranges

I have 2 range tables ReceivedRanges and DispatchedRanges.
ReceivedRanges
From - To
1 - 100000
200000 - 300000
350000 - 400000
DispatchedRanges
From - To
10000 - 50000
250000 - 275000
350000 - 400000
I want to select new ranges from above 2 tables. output ranges will be:
InventoryRanges
From - To
1 - 9999
50001 - 100000
200000 - 249999
275001 - 300000
how to select these ranges from tables of ranges.
What I have Tried:
Alternately I have tried with generating all individual received sequence numbers as sequence table and marked dispatched numbers as is dispatched true.
based on this table i am grouping and able to retrieve InventoryRanges. But for these I need to store all the sequence number and update huge ranges during dispatch which will slowdown the dispatch process.
I think this will cover all your cases of finding differences between ranges in SQL. Hope this helps you out:
create table received_ranges(item_id int, [from] int, [to] int);
create table dispatched_ranges(item_id int, [from] int, [to] int);
insert into received_ranges (item_id,[from],[to]) values
(1, 1,5000),
(1, 7000,8000),
(2, 6000,9000),
(3, 10000,15000),
(4, 20000,25000);
insert into dispatched_ranges (item_id,[from],[to]) values
(1, 1,250),
(2, 6000,7250),
(2, 7500,8000),
(2, 8200, 9000),
(3, 12000,14000),
(4, 20000,25000);
with dispatched_batch(dispatched_batch_num, received_item, received_from, received_to, dispatched_item, dispatched_from, dispatched_to) as
(select row_number() over (partition by rr.item_id, rr.[from] order by rr.[from]) dispatched_batch_num,
rr.item_id as received_item,
rr.[from] as received_from,
rr.[to] as received_to,
dr.item_id as dispatched_item,
dr.[from] as dispatched_from,
dr.[to] as dispatched_to
from received_ranges rr
left join
dispatched_ranges dr
ON
rr.item_id = dr.item_id
AND
dr.[from] >= rr.[from]
AND
dr.[to] <= rr.[to])
select * from
(select
[current].[received_item],
case when [next].dispatched_batch_num is null then
case when [current].[received_to] <> [current].[dispatched_to] then
[current].dispatched_to + 1
else
0
end
else
case when [next].[dispatched_from] <> [current].[dispatched_to]+1 then
[current].[dispatched_to] + 1
else
0
end
end
as 'inventory from',
case when [next].dispatched_batch_num is null then
case when [current].[received_to] <> [current].[dispatched_to] then
[current].received_to
else
0
end
else
case when [next].[dispatched_from] <> [current].[dispatched_to]+1 then
[next].[dispatched_from] - 1
else
0
end
end
as 'inventory to'
from dispatched_batch [current]
left join
dispatched_batch [next]
on
[current].received_item = [next].received_item
and
[current].dispatched_batch_num + 1 = [next].dispatched_batch_num
UNION
select
[current].received_item,
case when [current].[dispatched_from] is null then
[current].[received_from]
else
case when [previous].dispatched_batch_num is null then
case when [current].[received_from] <> [current].[dispatched_from] then
[current].received_from
else
0
end
else
case when [previous].[dispatched_to] <> [current].[dispatched_from]+1 then
[previous].[dispatched_to] + 1
else
0
end
end
end
as 'inventory from',
case when [current].[dispatched_to] is null then
[current].[received_to]
else
case when [previous].dispatched_batch_num is null then
case when [current].[received_from] <> [current].[dispatched_from] then
[current].dispatched_from -1
else
0
end
else
case when [previous].[dispatched_to] <> [current].[dispatched_from]+1 then
[current].[dispatched_from] - 1
else
0
end
end
end
as 'inventory to'
from dispatched_batch [current]
left join
dispatched_batch previous
on
[current].received_item = previous.received_item
and
[current].dispatched_batch_num = previous.dispatched_batch_num + 1) result
where [inventory from] <> 0;
Assuming your table structure to be something like this:
create table received_ranges(received_date date, [from] int, [to] int);
create table dispatched_ranges(dispatched_date date, [from] int, [to] int);
and also assuming that there wont be multiple entries for each date in received_ranges or dispatched_ranges table (assumption made from the data provided) this query should work for you:
select date, inventory_from, inventory_to from
(select
rr.received_date as 'date',
case when rr.[from] = dr.[from] then
0
else
rr.[from]
end AS 'inventory_from',
case when rr.[from] = dr.[from] then
0
else
dr.[from] - 1
end AS 'inventory_to'
from
received_ranges rr left join
dispatched_ranges dr
on
rr.received_date = dr.dispatched_date
UNION
Select
rr.received_date as 'date',
case when rr.[to] = dr.[to] then
0
else
dr.[to]+1
end AS 'inventory_from',
case when rr.[to] = dr.[to] then
0
else
rr.[to]
end AS 'inventory_to'
from
received_ranges rr left join
dispatched_ranges dr
on
rr.received_date = dr.dispatched_date) result
where (inventory_from == 0 and inventory_to == 0) <> TRUE
order by inventory_from;

Aggregate function in update query with case statement in sql server

I am trying to write update query by using aggregate function and case statement.
Some how i am stuck
Initially i have written following query which gave me error tha,"Aggregate function can not be used in update statement
UPDATE report
SET report.LoadDischargeQty =
CASE WHEN SUBSTRING(CRG_ArrDep,1,1) = 'D' AND cargo.CRG_Quantity is NOT NULL THEN cargo.CRG_Quantity
ELSE
CASE WHEN report.PlaId = 'LP' THEN
CASE WHEN SUBSTRING(CRG_ArrDep,1,1) = 'D' THEN ISNULL(SUM(CRG_SFgrMT),0) END -
CASE WHEN SUBSTRING(CRG_ArrDep,1,1) = 'A' THEN ISNULL(SUM(CRG_SFgrMT),0)END
WHEN report.PlaId = 'DP' THEN
CASE WHEN SUBSTRING(CRG_ArrDep,1,1) = 'A' THEN ISNULL(SUM(CRG_SFgrMT),0) END-
CASE WHEN SUBSTRING(CRG_ArrDep,1,1) = 'D' THEN ISNULL(SUM(CRG_SFgrMT),0)END
ELSE 0 END
END
from #CargoPerformanceReport report
INNER JOIN POSCARGO cargo ON cargo.POS_ID = report.PositionId AND ISNULL(cargo.CRG_Deleted,0)=0
So I refactored it as follow
UPDATE report
SET report.LoadDischargeQty =
CASE WHEN SUBSTRING(CRG_ArrDep,1,1) = 'D' AND cargo.CRG_Quantity is NOT NULL THEN cargo.CRG_Quantity
ELSE
select quantity.dischargeQuantity from (SELECT CASE WHEN report.PlaId = 'LP' THEN
CASE WHEN SUBSTRING(CRG_ArrDep,1,1) = 'D' THEN ISNULL(SUM(CRG_SFgrMT),0) END -
CASE WHEN SUBSTRING(CRG_ArrDep,1,1) = 'A' THEN ISNULL(SUM(CRG_SFgrMT),0)END
WHEN report.PlaId = 'DP' THEN
CASE WHEN SUBSTRING(CRG_ArrDep,1,1) = 'A' THEN ISNULL(SUM(CRG_SFgrMT),0) END-
CASE WHEN SUBSTRING(CRG_ArrDep,1,1) = 'D' THEN ISNULL(SUM(CRG_SFgrMT),0)END
ELSE 0 END dischargeQuantity ) quantity
END
from #CargoPerformanceReport report
INNER JOIN POSCARGO cargo ON cargo.POS_ID = report.PositionId AND ISNULL(cargo.CRG_Deleted,0)=0
table structure
CREATE TABLE #CargoPerformanceReport
(
PositionId VARCHAR(12),
PortAndActivityName VARCHAR(100),
PlaId VARCHAR(12),
LoadDischargeQty REAL,
);
Insert into #CargoPerformanceReport
Values('100',null,'LP',null)
CREATE TABLE #Poscargo
(
POS_ID VARCHAR(12),
CRG_ArrDep VARCHAR(3),
CRG_SFgrMT REAL,
CRG_Quantity REAL,
CRG_Deleted BIT
);
Insert Into #Poscargo(POS_ID,CRG_ArrDep,CRG_SFgrMT,null,0)
Values ('100','DD',100)
Insert Into #Poscargo(POS_ID,CRG_ArrDep,CRG_SFgrMT)
Values ('100','AD',100)
Insert Into #Poscargo(POS_ID,CRG_ArrDep,CRG_SFgrMT)
Values ('100','DD',200)
Insert Into #Poscargo(POS_ID,CRG_ArrDep,CRG_SFgrMT)
Values ('100','AD',50)
Insert Into #Poscargo(POS_ID,CRG_ArrDep,CRG_SFgrMT)
Values ('101','DL',200)
Insert Into #Poscargo(POS_ID,CRG_ArrDep,CRG_SFgrMT)
Values ('101','AL',200)
SELECT * FROM #Poscargo
SELECT * FROM #CargoPerformanceReport
result:-
PositionId | PlaId | LoadDischargeQty
100 | LP | 150
but it is not right way and also has error.
anyone have optimized solution for the same?
Put all that stuff into subquery:
SELECT LoadDischargeQty =
CASE
WHEN report.PlaId = 'LP'
THEN 1
ELSE -1
END * cargo.qty
FROM CargoPerformanceReport report
CROSS APPLY(
SELECT SUM(
CASE
WHEN CRG_ArrDep LIKE 'D%'
THEN 1
WHEN CRG_ArrDep LIKE 'A%'
THEN -1
ELSE 0
END * ISNULL(CRG_SFgrMT, 0)
) qty
FROM POSCARGO cargo
WHERE cargo.POS_ID = report.PositionId
AND ISNULL(cargo.CRG_Deleted,0)=0
) cargo
http://sqlfiddle.com/#!18/648d0/7
I don't understand how is CRG_Quantity column supposed to be used and you did not provide any row with that data so I removed it to show you the aggregation itself. Works fine, you'll get your 150. You may easily convert it into update statement.
Perhaps something like this?
It uses cases with summed cases.
Test on SQL Fiddle here
UPDATE t
SET LoadDischargeQty = q.CalcDischargeQty
FROM #CargoPerformanceReport t
JOIN
(
SELECT report.PositionId, report.PlaId,
CASE
WHEN SUM(CASE WHEN LEFT(cargo.CRG_ArrDep,1) = 'D' THEN cargo.CRG_Quantity END) IS NOT NULL
THEN SUM(CASE WHEN LEFT(cargo.CRG_ArrDep,1) = 'D' THEN cargo.CRG_Quantity END)
ELSE CASE
WHEN report.PlaId = 'LP'
THEN SUM(CASE WHEN LEFT(cargo.CRG_ArrDep,1) = 'D' THEN cargo.CRG_SFgrMT END) -
SUM(CASE WHEN LEFT(cargo.CRG_ArrDep,1) = 'A' THEN cargo.CRG_SFgrMT END)
WHEN report.PlaId = 'DP'
THEN SUM(CASE WHEN LEFT(cargo.CRG_ArrDep,1) = 'A' THEN cargo.CRG_SFgrMT END) -
SUM(CASE WHEN LEFT(cargo.CRG_ArrDep,1) = 'D' THEN cargo.CRG_SFgrMT END)
ELSE 0
END
END AS CalcDischargeQty
FROM #CargoPerformanceReport report
INNER JOIN #Poscargo cargo
ON cargo.POS_ID = report.PositionId AND (cargo.CRG_Deleted = 0 OR cargo.CRG_Deleted IS NULL)
GROUP BY report.PositionId, report.PlaId
) q ON t.PositionId = q.PositionId;
Sample Data
IF OBJECT_ID('tempdb..#CargoPerformanceReport') IS NOT NULL DROP TABLE #CargoPerformanceReport;
CREATE TABLE #CargoPerformanceReport
(
PositionId VARCHAR(12) PRIMARY KEY,
PortAndActivityName VARCHAR(100),
PlaId VARCHAR(12),
LoadDischargeQty REAL
);
IF OBJECT_ID('tempdb..#Poscargo') IS NOT NULL DROP TABLE #Poscargo;
CREATE TABLE #Poscargo
(
POS_ID VARCHAR(12),
CRG_ArrDep VARCHAR(3),
CRG_SFgrMT REAL,
CRG_Quantity REAL,
CRG_Deleted BIT
);
Insert into #CargoPerformanceReport Values
('100','name1','LP',null),
('101','name2','DP',null),
('102','name3','DP',null);
Insert Into #Poscargo(POS_ID, CRG_ArrDep, CRG_SFgrMT, CRG_Quantity, CRG_Deleted) Values
('100','DD',100,null,0)
,('100','AD',100,null,0)
,('100','DD',200,null,0)
,('100','AD',50,null,0)
,('101','DL',100,null,0)
,('101','AL',200,null,0)
,('102','DL',100,500,0)
,('102','AL',200,null,0);
Result
PositionId PortAndActivityName PlaId LoadDischargeQty
---------- ------------------- ----- ----------------
100 name1 LP 150
101 name2 DP 100
102 name3 DP 500

How to select on stored procedure with filter have more than 3 value

I would like to know that how to select when using stored procedure with filter as combobox in website have more than 3 value
Exp: I would like to select match listing. and filter is "IsFinish" with value (All, Yes, No)
DECLARE #IsFinish INT -- 1: All 2: Yes 3: No
SELECT * FROM MATCH
WHERE [Status] = ?
Status values: F: Finished C: Canceled L: Live N: Non-Live P:Pause X:(Close) Waiting Confirm
When select All the result will return all status.
When select Yes the result will return F & X.
When select No the result will return N, L, C, P.
I would like filter them by once select.
How can I do it?
Prepare Data
CREATE TABLE [Match] ([Status] CHAR(1) PRIMARY KEY, [Wording] VARCHAR(50));
INSERT INTO [Match] ([Status], [Wording]) VALUES
('F', 'Finished'),
('C', 'Canceled'),
('L', 'Live'),
('N', 'Non-Live'),
('P', 'Pause'),
('X', '(Close) Waiting Confirm');
Prepare Procedure
CREATE PROCEDURE [FilterMatch] (#IsFinish INT = 1 /* 1: All (Default) 2: Yes 3: No */)
AS
SELECT * FROM [Match]
WHERE #IsFinish = 1 OR
(#IsFinish = 2 AND [Status] In ('F','X')) OR
(#IsFinish = 3 AND [Status] In ('N','L','C','P'));
Run
Exec FilterMatch;
Exec FilterMatch 2;
Exec FilterMatch #IsFinish = 3;
In SQL-Server you can achieve It in following:
where [status] LIKE (case #IsFinish when 1 then '%' end) or
[status] = (case #IsFinish when 2 then 'F' end) or
[status] = (case #IsFinish when 2 then 'X' end) or
[status] = (case #IsFinish when 3 then 'N' end) or
[status] = (case #IsFinish when 3 then 'L' end) or
[status] = (case #IsFinish when 3 then 'C' end) or
[status] = (case #IsFinish when 3 then 'P' end)
Use IN and CASE and OR. Something like:
WHERE isFinish = 1
OR isFinish = 2 AND Status IN ('F','X')
OR isFinish = 3 AND Status IN ('N','L','C','P')
I have tried to using where case when with
where case when [status] IN ('L','N','F','X','C','P') then 0
when [status] in ('F','X') THEN 1
when [Status] IN ('L','N','C','P') THEN 2 END = #IsFinish
But I'm not feeling It look good.
I look forward to hearing from you in the nearest time.

Stored procedure to return data based on sum of 3 columns

I have a stored procedure as follows:
SELECT DiscountId
,CompanyId
,Discount1
,Discount2
,Discount3
,(Discount1+Discount2+Discount3) as Total
FROM PriceDiscount
This can return one or more rows.
I also need to check the aggregate of Discount1, Discount2 and Discount3. If the sum for any of the rows is greater than 0, I want to set a column isDiscounted = true and return it also.
Please help me - how can I achieve this?
I do not want to check this in the code or create another stored procedure for this. Hence wanted to return both the results from this stored procedure.
You can use case to calculate/set the isDiscounted column.
with x as (
SELECT DiscountId
,CompanyId
,Discount1
,Discount2
,Discount3
,case when (Discount1+Discount2+Discount3) > 0 then 'True'
else 'False' end as isDiscounted
FROM PriceDiscount)
, y as (select case when isDiscounted = 'True' then count(*) end as true_count,
case when isDiscounted = 'False' then count(*) end as false_count
from x group by isDiscounted)
select case when true_count > 0 then 'True'
when falsecount > 0 and truecount = 0 then 'False' end
as final_status
from y
Edit:
with x as (
SELECT DiscountId
,CompanyId
,Discount1
,Discount2
,Discount3
,case when (Discount1+Discount2+Discount3) > 0 then 'True'
else 'False' end as isDiscounted
FROM PriceDiscount)
, y as (select sum(case when isDiscounted = 'True' then 1 else 0 end) as true_count,
sum(case when isDiscounted = 'False' then 1 else 0 end) as false_count
from x)
select case when true_count > 0 then 'True'
when false_count > 0 and true_count = 0 then 'False' end
as final_status
from y