How to count column values in table row? - sql-server

In SQL Server, i have one table in that there 15 columns are there, the columns contain the data SL,PL,CL or 8,4 like that.
empid D1 D2 D3 D4 D5 D6 D7 D8 D9 D10 D11 D12 D13 D14 D15
==================================================================
sam PL 8 PL 4 8 SL 8 SL 4 CL 8 CL 8 8 8
sunny 8 CL 4 CL SL 8 4 SL 4 PL 8 8 8 8 8
zimmy 4 4 4 8 8 8 4 4 8 8 8 8 8 8 8
Like the above my table is there , now i want to calculate PL or CL or SL of every empid, please help me.

The need to aggregate across columns in a single row is a good indicator you're storing the data wrong. I would change it to the following:
empid D_number val
sam 1 PL
sam 2 8
sam 3 PL
In which case you can just do a count or conditional sum. For example, a conditional sum to get both PL and SL counts in one go:
select empid, sum(case when val = 'PL' then 1 else 0 end),
sum(case when val = 'SL' then 1 else 0 end)
from myTable
group by empid
Or if you're just interested in the PL count, you can simply COUNT:
select empid, count(*)
from myTable
where val = 'PL'
group by empid
However, assuming there's nothing you can do about your table schema, you have two options:
Conditional sum across each column explicitly (boy does this get ugly fast):
select empid,
case when d1 = 'PL' then 1 else 0 end +
case when d2 = 'PL' then 1 else 0 end +
case when d3 = 'PL' then 1 else 0 end +
...,
case when d1 = 'SL' then 1 else 0 end +
case when d2 = 'SL' then 1 else 0 end +
case when d3 = 'SL' then 1 else 0 end +
...
from myTable
UNPIVOT the data to make it look like the form above, then use a conditional sum:
with cte as (
select empid, D_number, val
from (
select empid, d1, d2, d3, d4, d5, ...
from myTable
) x
unpivot (val for D_number in
(d1, d2, d3, d4, d5, ...)
)
)
select empid, sum(case when val = 'PL' then 1 else 0 end),
sum(case when val = 'SL' then 1 else 0 end)
from cte
group by empid

Use UNPIVOT
Test data and table:
DECLARE #t table
(empid varchar(10), D1 char(2), D2 char(2), D3 char(2), D4 char(2),D5 char(2),
D6 char(2),D7 char(2), D8 char(2), D9 char(2),D10 char(2), D11 char(2),
D12 char(2), D13 char(2), D14 char(2), D15 char(2))
INSERT #t values
('sam', 'PL','8','PL','4','8','SL','8','SL','4','CL','8','CL','8','8','8'),
('sunny','8','CL','4','CL','SL','8','4','SL','4','PL','8','8','8','8','8'),
('zimmy','4','4','4','8','8','8','4','4','8','8','8','8','8','8','8')
Query:
SELECT empid, value, count(*) count
FROM #t as p
UNPIVOT
(value FOR col IN
([D1],[D2],[D3],[D4],[D5],[D6],[D7],[D8],[D9],
[D10],[D11],[D12],[D13],[D14],[D15]) ) AS unpvt
WHERE value in ('PL','CL','SL')
GROUP BY empid, value
Result:
empid value count
sam CL 2
sam PL 2
sam SL 2
sunny CL 2
sunny PL 1
sunny SL 2

Related

How to divide the product quantity based on the group quantity

I have a table with list of products with quantities and their group. I want to divide them equally based on the product group quantity. Each group may contain one or more products.
The following table shows the products and their group wise quantity
SortOrder ProductID ToolGroup ToolGroupQty Quantity
1 PRD1 A1 180 900
2 PRD2 A2 77 125
3 PRD3 A2 125
4 PRD4 A2 135
5 PRD5 A3 129 125
6 PRD6 A3 520
7 PRD7 A4 77 385
The actual result should be as follows
SortOrder ProductID ToolGroup Quantity Group
1 PRD1 A1 180 1
2 PRD2 A2 77 1
5 PRD5 A3 125 1
6 PRD6 A3 4 1
7 PRD7 A4 77 1
1 PRD1 A1 180 2
2 PRD2 A2 48 2
3 PRD3 A2 29 2
6 PRD6 A3 129 2
7 PRD7 A4 77 2
1 PRD1 A1 180 3
3 PRD3 A2 77 3
6 PRD6 A3 129 3
7 PRD7 A4 77 3
1 PRD1 A1 180 4
3 PRD3 A2 19 4
4 PRD4 A2 58 4
6 PRD6 A3 129 4
7 PRD7 A4 77 4
1 PRD1 A1 180 5
4 PRD4 A2 77 5
6 PRD6 A3 129 5
7 PRD7 A4 77 5
The sum of each group should be equal. For this case 463. Each ToolGroup's SUM should be equal to the respective toolgroup quantity.
The sum of each product should be equal to the given quantity in the above table.
Please help me on this. I have tried many approaches. Nothing worked out for me.
Please find the code I tried below
declare #CombinationGroupTable table(SortOrder int,ProductID nvarchar(50),Combination nvarchar(20),Tools int,ToolGroup nvarchar(10),ToolGroupQty int,Market nvarchar(20),Quantity int,isUpdated char(10))
insert into #CombinationGroupTable values(1,'PRD1','A',7,'A1', 180,'M0002',900,NULL)
insert into #CombinationGroupTable values(2,'PRD2','A',3,'A2', 77, 'M0003',125,NULL)
insert into #CombinationGroupTable values(3,'PRD3','A',NULL,'A2', NULL,'M0004',125,NULL)
insert into #CombinationGroupTable values(4,'PRD4','A',NULL,'A2', NULL,'M0004',135,NULL)
insert into #CombinationGroupTable values(5,'PRD5','A',5,'A3', 128,'M0001',125,NULL)
insert into #CombinationGroupTable values(6,'PRD6','A',NULL,'A3',NULL, 'M0003',520,NULL)
insert into #CombinationGroupTable values(7,'PRD7','A',3,'A4', 77,'M0004', 385, NULL)
select * from #CombinationGroupTable
declare #SortOrder int,#productID nvarchar(100),#Quantity int,#shift char(1),#prevQty int,#productCode nvarchar(100)
declare #Combination nvarchar(20),#Market nvarchar(50),#Tools int, #prevTools int,#prevComb nvarchar(10), #ToolGroupName nvarchar(20),#tGroupCount int
declare #MaxgroupID nvarchar(20),#NextGroup nvarchar(20), #MaxComb int,#LastSortOrder int,#toCompensate int,#ToolGroup nvarchar(20), #ToolGroupQty int
declare #minOrder int , #maxOrder int, #combProdID nvarchar(100), #combMarket nvarchar(20), #combQty int, #shiftFact int,#combTools int,#combToolsGroup nvarchar(10), #ToolQty int, #toolshiftQty int,#combOrder int, #CToolGroup nvarchar(20)
declare #shiftQty int = 464,#ToolsCount int = 18
declare #ProdQty table(ID int identity(1,1),SortOrder int,ProductID nvarchar(100),Quantity int,Market nvarchar(10),GroupNo int,ToolGroup nvarchar(20))
declare #RID int,#SOrder int,#CCombination nvarchar(20), #CTotal int, #CompensationQty int,#LastQty int,#RemaininQty int,#PreviousQty int,#ctoolgroupQty int, #tgCompensate int
declare planSchedule cursor for select SortOrder,ProductID,Combination,Tools,ToolGroup,ToolGroupQty,Market,Quantity from #CombinationGroupTable order by SortOrder
open planSchedule
fetch next from planSchedule into #sortOrder,#ProductID,#Combination,#Tools,#ToolGroup,#ToolGroupQty,#Market,#Quantity
while ##FETCH_STATUS=0
begin
select top 1 #MaxComb = isnull(GroupNo,1) from #ProdQty group by GroupNo Order by CAST(GroupNo as int) desc
set #NextGroup= case when isnull(#LastQty,0) < #shiftQty then isnull(#MaxComb,1) else #MaxComb+1 end
select #minOrder= MIN(SortOrder),#maxOrder = MAX(SortOrder) from #CombinationGroupTable
while #minOrder <= #maxOrder
begin
select #combMarket= Market,#combQty = Quantity,#combProdID = ProductID,#combTools= Tools,#combToolsGroup= toolGroup,#ctoolgroupQty= ToolGroupQty from #CombinationGroupTable where Combination = #Combination and SortOrder= #minOrder and tools is not null
select #ToolQty = CASE WHEN #LastQty < #shiftQty THEN (CAST(#shiftQty-#LastQty as numeric)/#ToolsCount * #combTools) ELSE (CAST(#shiftQty as numeric)/#ToolsCount * #combTools) END
if(isnull(#Tools,'') <> '' and isnull(#combTools,'') <> '')
begin
if((select count(*) from #CombinationGroupTable where ToolGroup = #ToolGroup and Sortorder = #minOrder)> 1)
select count(*),ToolGroup from #CombinationGroupTable where ToolGroup = #ToolGroup and Sortorder = #minOrder group by ToolGroup
else
begin
if(#combQty >= #ToolQty)
begin
if((select isnull(sum(quantity),0) from #ProdQty where ToolGroup = #combToolsGroup and GroupNo = #NextGroup) <= #ctoolgroupQty)
begin
insert into #ProdQty values(#minOrder,#combProdID,#ToolQty,#combMarket,#NextGroup,#combToolsGroup)
end
else
begin
set #tgCompensate = #ToolQty -(select sum(quantity) from #ProdQty where groupno = #nextgroup and ToolGroup = #combToolsGroup)
insert into #ProdQty values(#minOrder,#combProdID,#tgCompensate,#combMarket,#NextGroup,#combToolsGroup)
update #CombinationGroupTable set Quantity= Quantity - #tgCompensate,ToolGroupQty= #ToolQty,isUpdated='Y' where productID= #combProdID and ToolGroup = #combToolsGroup
end
end
else
begin
insert into #ProdQty values(#minOrder,#combProdID,#combQty,#combMarket,#NextGroup,#combToolsGroup)
update #CombinationGroupTable set Tools = #Tools where ToolGroup= #ToolGroup and isnull(isUpdated,'N')='N' and SortOrder= #minOrder + 1
end
end
update #CombinationGroupTable set Quantity = case when #combQty >= #ToolQty then (Quantity-#ToolQty) else (Quantity-#combQty) end,isUpdated='Y' where ProductID = #combProdID
end
set #minOrder= #minOrder+1
set #combMarket= '' set #combQty = 0 set #combProdID = '' set #combTools = 0
end
set #LastQty = 500000
fetch next from planSchedule into #sortOrder,#ProductID,#Combination,#Tools,#ToolGroup,#ToolGroupQty,#Market,#Quantity
end
close planSchedule
deallocate planSchedule
select SortOrder,ProductID,ToolGroup,Quantity,GroupNo [Group] from #ProdQty
Expected result in excel format
The query which you uses has a CURSOR which is very expensive in terms of computation cost, I'd recommend using the RECURSIVE CTE approach to divide the product quantity.
Assumptions I made are
ToolGroupQty field should have values in it, you can auto fill the values in advance
The following query should do what you want:
DECLARE #CombinationGroupTable TABLE (SortOrder INT,ProductID NVARCHAR(50),Combination NVARCHAR(20),Tools INT,ToolGroup NVARCHAR(10),ToolGroupQty INT,Market NVARCHAR(20),Quantity INT,isUpdated CHAR(10))
INSERT INTO #CombinationGroupTable VALUES(1,'PRD1','A',7,'A1', 180,'M0002',900,NULL)
INSERT INTO #CombinationGroupTable VALUES(2,'PRD2','A',3,'A2', 77, 'M0003',125,NULL)
INSERT INTO #CombinationGroupTable VALUES(3,'PRD3','A',NULL,'A2', 77,'M0004',125,NULL)
INSERT INTO #CombinationGroupTable VALUES(4,'PRD4','A',NULL,'A2', 77,'M0004',135,NULL)
INSERT INTO #CombinationGroupTable VALUES(5,'PRD5','A',5,'A3', 128,'M0001',125,NULL)
INSERT INTO #CombinationGroupTable VALUES(6,'PRD6','A',NULL,'A3',128, 'M0003',520,NULL)
INSERT INTO #CombinationGroupTable VALUES(7,'PRD7','A',3,'A4', 77,'M0004', 385, NULL)
;WITH cte (ProductID, ToolGroupQty, Quantity, Quantity_calc, [Group]) as (
SELECT ProductID
,ToolGroupQty
,Quantity
,CASE WHEN Quantity>ToolGroupQty THEN ToolGroupQty
ELSE Quantity END
,1
FROM #CombinationGroupTable
UNION ALL
SELECT ProductID
,ToolGroupQty
,t1
,CASE WHEN t1>ToolGroupQty THEN ToolGroupQty
ELSE t1 END
,[Group] + 1
FROM cte
CROSS APPLY (VALUES(Quantity-ToolGroupQty)) AS t(t1) WHERE t1 > 0
)
SELECT ProductID
,ToolGroupQty
,Quantity_calc AS [Quantity]
,[Group]
FROM cte
ORDER BY [Group], [ProductID]
The result is as below,
ProductID ToolGroupQty Quantity Group
PRD1 180 180 1
PRD2 77 77 1
PRD3 77 77 1
PRD4 77 77 1
PRD5 128 125 1
PRD6 128 128 1
PRD7 77 77 1
PRD1 180 180 2
PRD2 77 48 2
PRD3 77 48 2
PRD4 77 58 2
PRD6 128 128 2
PRD7 77 77 2
PRD1 180 180 3
PRD6 128 128 3
PRD7 77 77 3
PRD1 180 180 4
PRD6 128 128 4
PRD7 77 77 4
PRD1 180 180 5
PRD6 128 8 5
PRD7 77 77 5

How to insert "empty" row extracting a month list?

I've this sp, which return a list of data, for each "month" (i.e. each row is a month). Somethings like that:
SELECT
*,
(CAST(t1.NumActivities AS DECIMAL) / t1.NumVisits) * 100 AS PercAccepted,
(CAST(t1.Accepted AS DECIMAL) / t1.Estimated) * 100 AS PercValue
FROM
(SELECT
MONTH(DateVisit) AS Month,
COUNT(*) AS NumVisits,
SUM(CASE WHEN DateActivity is not null THEN 1 ELSE 0 END) AS NumActivities,
SUM(Estimate) AS Estimated,
SUM(CASE WHEN DateActivity is not null THEN Estimate ELSE 0 END) AS Accepted
FROM [dbo].[Activities]
WHERE
DateVisit IS NOT NULL
AND (#year IS NULL OR YEAR(DateVisit) = #year)
AND (#clinicID IS NULL OR ClinicID = #clinicID)
GROUP BY MONTH(DateVisit)) t1
This is a result:
Month NumVisits NumActivities Estimated Accepted PercAccepted PercValue
1 5 1 13770.00 2520.00 20.00000000000 18.30065359477124
2 2 2 7900.00 7900.00 100.00000000000 100.00000000000000
3 1 0 2730.00 0.00 0.00000000000 0.00000000000000
8 1 1 3000.00 3000.00 100.00000000000 100.00000000000000
But as you can see, I could "miss" some Month (for example, here April "4" is missed).
Is it possible to insert, for the missing month/row, an empty (0) record? Such as:
Month NumVisits NumActivities Estimated Accepted PercAccepted PercValue
1 5 1 13770.00 2520.00 20.00000000000 18.30065359477124
2 2 2 7900.00 7900.00 100.00000000000 100.00000000000000
3 1 0 2730.00 0.00 0.00000000000 0.00000000000000
4 0 0 0 0 0 0
...
Here is a example with sample data:
CREATE TABLE #Report
(
Id INT,
Name nvarchar(max),
Percentage float
)
INSERT INTO #Report VALUES (1,'ONE',2.01)
INSERT INTO #Report VALUES (2,'TWO',3.01)
INSERT INTO #Report VALUES (5,'Five',5.01)
;WITH months(Month) AS
(
SELECT 1
UNION ALL
SELECT Month+1
FROM months
WHERE Month < 12
)
SELECT *
INTO #AllMonthsNumber
from months;
Your select query:
The left join will gives you the NULL for other months so just use ISNULL('ColumnName','String_to_replace')
\/\/\/\/
SELECT Month, ISNULL(Name,0), ISNULL(Percentage,0)
FROM AllMonthsNumber A
LEFT JOIN #Report B
ON A.Month = B.Id
EDIT:
Yes you can do it without creating AllMonthNumber Table:
You can use master..spt_values (found here) system table which contains the numbers so just with some where condition.
SELECT Number as Month, ISNULL(B.Name,0), ISNULL(Percentage,0)
FROM master..spt_values A
LEFT JOIN #Report B ON A.Number = B.Id
WHERE Type = 'P' AND number BETWEEN 1 AND 12

How to derive a column based on multiple columns values for a single row

I currently have an issue with a calculation I'm trying to create.
So I have 8 columns, all with the same 3 values:
1 = pass , 2 = borderline , 3 = Fail.
I need to be able to calculate a new column which will state the following:
Pass = All 8 fields equals '1'
Borderline = 7 Fields that equal '1' with one field that equals '2'
Else Fail
It seems a easy calculation but I keep tripping up on the borderline logic, as any of the 8 columns could have the '2'?
Any help would be much appreciated.
If your columns will have only 1,2 or 3 then you can try this
case Col1 + Col2 + Col3 + Col4 + Col5 + Col6 + Col7 + Col8 When 8 Then 'Pass'
when 9 Then 'Borderline'
Else 'Fail'
End
You could simple calculate the total of the 8 columns, and if the value is greater than 9 it's a fail. This assumes no NULL or 0 values are in the 8 columns.
SELECT Col1 + Col2 + Col3 + Col4 + Col5 + Col6 + Col7 + Col8 AS Total
You can use simple addition with a CASE statement to work this out.
Rules:
8 passes: total always = 8
7 passes and 1 borderline: always = 9
Any other combination = fail
Sample code:
-- dummy table
CREATE TABLE #temp
(
c1 INT , c2 INT , c3 INT , c4 INT ,
c5 INT , c6 INT , c7 INT , c8 INT
)
-- dummy data
INSERT INTO #temp
( c1 , c2 , c3 , c4 ,
c5 , c6 , c7 , c8
)
SELECT 1,1,1,1,1,1,1,1
UNION ALL
SELECT 1,1,1,1,1,1,2,1
UNION ALL
SELECT 1,2,3,1,1,1,1,2
-- query to calculate totals and pass/fail
SELECT *,
CASE t.Total WHEN 8 THEN 'Pass'
WHEN 9 THEN 'Border Line'
ELSE 'Fail' END Overall
FROM ( SELECT * ,
c1 + c2 + c3 + c4 + c5 + c6
+ c7 + c8 Total
FROM #temp
) t
-- clean up
DROP TABLE #temp
Produces:
c1 c2 c3 c4 c5 c6 c7 c8 Total Overall
1 1 1 1 1 1 1 1 8 Pass
1 1 1 1 1 1 2 1 9 Border Line
1 2 3 1 1 1 1 2 12 Fail

Getting records from a table which have non-null values for a particular column in all months

I have a table:
Project_Id Period Value
123 Jan-15 0
123 Feb-15 34
123 Mar-15 78
123 Apr-15 56
456 Jan-15 0
456 Feb-15 0
456 Mar-15 0
456 Apr-15 0
789 Jan-15 45
789 Feb-15 4
789 Mar-15 18
789 Apr-15 26
I need to retrieve Project data only when i do not have 0 for Value field in all the months like:
Project_Id Period Value
123 Jan-15 0
123 Feb-15 34
123 Mar-15 78
123 Apr-15 56
789 Jan-15 45
789 Feb-15 4
789 Mar-15 18
789 Apr-15 26
Project no 456 should not come in my result because for all the months the value is 0 for that particular project.
Can someone help me with the query?
Use SUM and COUNT to determine the number of 0 Values:
SELECT *
FROM tbl
WHERE project_id IN(
SELECT project_id
FROM tbl
GROUP BY project_id
HAVING SUM(CASE WHEN Value = 0 THEN 1 ELSE 0 END) <> COUNT(*)
)
SQL Fiddle
Another solution is to use EXISTS:
SELECT *
FROM tbl t1
WHERE EXISTS(
SELECT 1 FROM tbl t2 WHERE t2.project_id = t1.project_id AND t2.Value > 0
)
SQL Fiddle
The inner select gets all project_ids that have a least one value that is not 0.
select * from your_table
where project_id in
(
select project_id
from your_table
group by project_id
having sum(case when value <> 0 then 1 else 0 end) > 0
)
Some test data but idea remains the same
create table #test123
(
pid int,
value int
)
insert into #test123
select 1,0
union all
select 1,1
union all
select 2,0
union all
select 2,0
union all
select 3,2
select * from #test123 t2 where exists (select 1 from #test123 t1
where t1.pid=t2.pid
group by pid
having sum(value)>0
)
For performance, I prefer not making a join to check for repeating values:
;WITH CTE as
(
SELECT
Project_Id,
Period,
Value,
max(abs(value)) over (Partition by Period) value
FROM YourTable
)
SELECT
Project_Id,
Period,
Value
FROM CTE
WHERE value > 0
*using abs to check for negative values. If all values are positive, the abs can be omitted.

Group by month in 6 month range sql server

I have a query that groups data by month, but there are some months that do not display simply because there is no data to display/group by. Is it possible to return the months and 0s for those months?
Here is my query
DECLARE #IMPORT_DATE AS DATETIME
SET #IMPORT_DATE = GETDATE()
SELECT COALESCE(COUNT(*),0), RIGHT(YEAR_MONTH_VALUE,2)
FROM VW_CALLS
WHERE CLIENT_ID = 2
AND START_DATETIME BETWEEN DATEADD("m", -5, #IMPORT_DATE) AND #IMPORT_DATE
GROUP BY YEAR_MONTH_VALUE
ORDER BY YEAR_MONTH_VALUE
And it returns this:
(No column name) (No column name)
740 11
1929 12
3864 01
But I would like this:
(No column name) (No column name)
0 08
0 09
0 10
740 11
1929 12
3864 01
You can use a recursive CTE like this:
DECLARE #MonthStart CHAR(2) = '08'
;WITH MonthsRange AS (
SELECT m = #MonthStart, rn = 1
UNION ALL
SELECT m = (CASE WHEN m = '12' THEN CAST('01' AS CHAR(2))
ELSE CAST(REPLICATE('0', 2 - LEN(CAST((CAST(m AS INT) + 1) AS CHAR(2)))) +
CAST((CAST(m AS INT) + 1) AS CHAR(2)) AS CHAR(2))
END),
rn = rn + 1
FROM MonthsRange
WHERE rn < 6
)
SELECT *
FROM MonthsRange
in order to get all months you want to include in your final result set:
m rn
=======
08 1
09 2
10 3
11 4
12 5
01 6
Now you can left join the above CTE against you query to get the result set you want:
;WITH MonthsRange AS (
... cte statements here
)
SELECT COALESCE(vw.cnt, 0) AS cnt, mr.m AS [Month]
FROM MonthsRange AS mr
LEFT JOIN (
SELECT COALESCE(COUNT(*),0) AS cnt, RIGHT(YEAR_MONTH_VALUE,2) AS m,
YEAR_MONTH_VALUE AS ym
FROM VW_CALLS
WHERE CLIENT_ID = 2 AND
START_DATETIME BETWEEN DATEADD("m", -5, #IMPORT_DATE) AND #IMPORT_DATE
GROUP BY YEAR_MONTH_VALUE) AS vw ON mr.m = vw.m
ORDER BY vw.ym

Resources