How to pivot by two columns into predefined table? - sql-server

I've seen this question, this and this, however these are not what I want.
Please, do not close my question as it is not duplicate. It is really important to me.
I've managed to pivot a table, but this is not desired result:
The desired result looks like this:
My sample data is(it is just an example as OperatorX and OC columns should be 50 times):
DECLARE #OperatorPrice TABLE (ID INT NOT NULL, OperatorId INT NULL, Price
NUMERIC(18,3) NULL, FName VARCHAR(50) NULL)
INSERT INTO #OperatorPrice (
ID, OperatorId, Price, FName
)
VALUES
(226, 996, 22954,'Operator1')
, (266, 1016, 79011.2, 'Operator3')
, (112, 1029, 14869, 'Operator4')
, (93, 1031, 10568.96, 'Operator5')
DECLARE #TR TABLE
(
ID INT NULL ,
Operator1 DECIMAL(18,3) NULL, OC1 DECIMAL(18,3) NULL, Operator2 DECIMAL(18,3) NULL,
OC2 DECIMAL(18,3) NULL, Operator3 DECIMAL(18,3) NULL, OC3 DECIMAL(18,3) NULL,
Operator4 DECIMAL(18,3) NULL, OC4 DECIMAL(18,3) NULL, Operator5 DECIMAL(18,3) NULL,
OC5 DECIMAL(18,3) NULL
)
Example code:
INSERT #TR
(ID ,
Operator1, OC1, Operator2, OC2, Operator3, OC3, Operator4, OC4,
Operator5, OC5)
SELECT ID ,
Operator1, OC1, Operator2, OC2, Operator3, OC3, Operator4, OC4,
Operator5, OC5
FROM
(SELECT Price, id, FName
FROM #OperatorPrice) AS SourceTable
PIVOT
(
sum(Price)
FOR FName IN (Operator1, OC1, Operator2, OC2, Operator3, OC3,
Operator4, OC4, Operator5, OC5)
) AS PivotTable
SELECT * FROM #TR
How can I insert data into OC columns?

Perhaps something like this.
In your alias SOURCETABLE, we just add a UNION ALL of possible combinations for an ID with NULL values. In this case the MIN(ID) and values 1 - 50
Just be sure to
1) Define #TR with columns Operator1,OC1,..,Operator50,OC50 <<< OC# can be an INT
2) in the FOR Item IN(Operator1,OC1,..,Operator50,OC50)
Example -- Edit Corrected to allow for >9 operators
INSERT #TR
SELECT *
FROM (
Select A.ID
,B.*
From #OperatorPrice A
Cross Apply ( values (FName,Price)
,('OC'+replace(FName,'Operator',''),OperatorID)
) B (Item,Value)
Union All
Select ID=(select min(ID) From #OperatorPrice)
,B.*
From ( Select Top 50 N=Row_Number() Over (Order By (Select NULL)) From master..spt_values n1 ) A
Cross Apply ( values (concat('Operator',N),NULL)
,(concat('OC',N),NULL)
) B (Item,Value)
) AS SourceTable
PIVOT ( sum(Value) FOR Item IN (Operator1, OC1, Operator2, OC2, Operator3, OC3, Operator4, OC4, Operator5, OC5) ) AS PivotTable
Select * from #TR
Returns -- Notice Operator2

Using your sample tables and data this is pretty easy with conditional aggregation. Not really clear though how you determine which operator number is which. Hopefully you have something better than parsing numbers out of the values but who knows.
select op.ID
, Operator1 = max(case when convert(int, replace(FName, 'Operator', '')) = 1 then Price end)
, OC1 = max(case when convert(int, replace(FName, 'Operator', '')) = 1 then OperatorID end)
, Operator2 = max(case when convert(int, replace(FName, 'Operator', '')) = 2 then Price end)
, OC2 = max(case when convert(int, replace(FName, 'Operator', '')) = 2 then OperatorID end)
, Operator3 = max(case when convert(int, replace(FName, 'Operator', '')) = 3 then Price end)
, OC3 = max(case when convert(int, replace(FName, 'Operator', '')) = 3 then OperatorID end)
, Operator4 = max(case when convert(int, replace(FName, 'Operator', '')) = 4 then Price end)
, OC4 = max(case when convert(int, replace(FName, 'Operator', '')) = 4 then OperatorID end)
, Operator5 = max(case when convert(int, replace(FName, 'Operator', '')) = 5 then Price end)
, OC5 = max(case when convert(int, replace(FName, 'Operator', '')) = 5 then OperatorID end)
from #OperatorPrice op
cross apply
(
values
(1)
,(2)
,(3)
,(4)
)x(N)
group by op.ID

Related

How to group ID's by score in different types in SQL Server

I have a table like this:
ID Type Score
-------------------
5 1 100
8 1 200
3 1 300
8 2 100
3 2 200
5 2 300
How do I sort them by descending score (to give them a ranking for that Type) and then create a table which a column for each Type where the ID's positions are shown such as:
ID Type1 Type2
--------------------
3 1st 2nd
5 3rd 1st
8 2nd 3rd
So far I am able able to do this by explicitly declaring the Type number such as:
SELECT ROW_NUMBER() OVER(ORDER BY Score DESC) AS Rank, ID
FROM Table
WHERE Type = 1
This returns a rank for each ID when Type is 1.
How do I join this together with the same result when Type is 2? And how do I do this for any number of types?
There are a number of ways to tackled this. My choice would be to use conditional aggregation. Here is how this might look. If you need a dynamic number of types that can be accomplished also but is a little trickier.
declare #Something table
(
ID int
, Type int
, Score int
)
;
insert #Something values
(5, 1, 100)
, (8, 1, 200)
, (3, 1, 300)
, (8, 2, 100)
, (3, 2, 200)
, (5, 2, 300)
;
with SortedValues as
(
select *
, RowNum = ROW_NUMBER() over (partition by Type order by Score)
from #Something
)
select ID
, Type1 = max(case when Type = 1 then RowNum end)
, Type2 = max(case when Type = 2 then RowNum end)
from SortedValues
group by ID
order by ID
;
-- EDIT --
I realized you said you need to have this work for any number of Types. Most people around SO like to ue a dynamic pivot. I personally find the syntax for pivot to be very obtuse. I prefer to build a dynamic version of conditional aggregation for this type of thing. Here is how you can use dynamic sql to generate the results for any number of Types.
Note I had to switch to using a temp table because a table variable would not be available in the scope of the dynamic sql unless it is declared inside the dynamic sql.
if OBJECT_ID('tempdb..#Something') is not null
drop table #Something
create table #Something
(
ID int
, Type int
, Score int
)
;
insert #Something values
(5, 1, 100)
, (8, 1, 200)
, (3, 1, 300)
, (8, 2, 100)
, (3, 2, 200)
, (5, 2, 300)
;
declare #StaticPortion nvarchar(2000) =
'with SortedValues as
(
select *, ROW_NUMBER() over(partition by Type order by Score) as RowNum
from #Something
)
select ID';
declare #DynamicPortion nvarchar(max) = '';
with E1(N) AS (select 1 from (values (1),(1),(1),(1),(1),(1),(1),(1),(1),(1))dt(n)),
E2(N) AS (SELECT 1 FROM E1 a, E1 b), --10E+2 or 100 rows
cteTally(N) AS
(
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E2
)
select #DynamicPortion = #DynamicPortion +
', MAX(Case when Type = ' + CAST(N as varchar(6)) + ' then RowNum end) as Type' + CAST(N as varchar(6)) + CHAR(10)
from cteTally t
where t.N <=
(
select Count(distinct Type)
from #Something
)
declare #FinalStaticPortion nvarchar(2000) = ' from SortedValues
group by ID
order by ID';
select #StaticPortion + #DynamicPortion + #FinalStaticPortion
declare #SqlToExecute nvarchar(max) = #StaticPortion + #DynamicPortion + #FinalStaticPortion;
select #SqlToExecute
exec sp_executesql #SqlToExecute

SQL Server sum query every 06:30 AM

Really need some help: I have a SQL Server table like this :
And I want to do a query to count value everyday start from 06:30 today until 06:29 tomorrow.
The problem is, the value does not start from 0 every 06:30.
I need query like this: Select maximum value per code first, ex :
(code : value)
M12 : 108,
M77 : 26
then the M77 value not start from 06:30.
I need to do : 26 - 12 (value before 06:30:00), so the M77 now is 14.
Finally sum the M77 and M12 = 14 + 108 = 122.
My expected output only for total value per date.
Based on my DB (image), the total value will be 26 - 12 =14.
26 is the latest value.
12 is value before 06:30
How to do it in SQL Server? Please help me
if i do understand you requirement correctly.
The query explanation are in code comments
; with
-- for getting the max value by code, rn = 1 is the max value
cte1 as
(
select *, rn = row_number() over (partition by code order by value desc)
from sample_table
),
-- for getting rows before 6:30, rn = 1 is the last record before 6:30
cte2 as
(
select *, rn = row_number() over (partition by code order by [date] desc)
from sample_table
where convert(time, [date]) < '06:30'
)
select total = sum(case when convert(time, c1.[date]) < '06:30'
then c1.value
else c1.value - c2.value
end)
from cte1 c1
left join cte2 c2 on c1.code = c2.code
and c1.rn = c2.rn
where c1.rn = 1
Try this
SELECT SUM([output]) AS [output]
FROM (
SELECT [latest]
,[before]
,(CASE WHEN [latest] = [before] THEN [latest]
ELSE [latest] - [before] END) AS [output]
FROM (
SELECT [code]
,MAX([value]) AS [latest]
,(SELECT MAX([value])
FROM [table]
WHERE [date] &lt CONVERT(datetime, CONVERT(varchar(10), CONVERT(date, [date])) + ' 06:30:00:000')
AND [code] = [t].[code]
GROUP BY [code]) AS [before]
FROM [table] AS [t]
GROUP BY [code]) AS [src]
) AS [rpt]
Try this puzzle:
CREATE TABLE TestTable
(
[date] datetime,
value int,
code varchar(10)
)
GO
INSERT INTO [dbo].[TestTable]
([date]
,[value]
,[code])
VALUES
('2018-09-13 06:20:52.803'
,100
,'M12'),
('2018-09-13 06:21:52.803'
,102
,'M12')
, ('2018-09-13 06:22:52.803'
,104
,'M12')
, ('2018-09-13 06:23:52.803'
,106
,'M12')
, ('2018-09-13 06:24:52.803'
,108
,'M12')
, ('2018-09-13 06:25:52.803'
,2
,'M77')
, ('2018-09-13 06:29:14.803'
,4
,'M77')
, ('2018-09-13 06:29:16.803'
,6
,'M77')
, ('2018-09-13 06:29:18.803'
,8
,'M77')
, ('2018-09-13 06:29:45.803'
,10
,'M77')
, ('2018-09-13 06:29:55.803'
,12
,'M77')
, ('2018-09-13 06:30:18.803'
,14
,'M77')
, ('2018-09-13 06:31:18.803'
,26
,'M77')
;WITH RESULT AS (
SELECT
TT.code
, MaxValueThatday = max(maxval.MAXVALUE )
, MaxValueBefore630NextDay = max(MAXValBefore630.MAXVALUE)
, ResultSubstraction =
CASE WHEN max(maxval.MAXVALUE ) <> max(MAXValBefore630.MAXVALUE)
THEN max(maxval.MAXVALUE ) - max(MAXValBefore630.MAXVALUE)
ELSE max(maxval.MAXVALUE )
END
FROM [dbo].[TestTable] TT
OUTER APPLY(
SELECT max(VALUE) MAXVALUE
, code
FROM [dbo].[TestTable] aa
WHERE Aa.code = tt.code
group by code
)maxval
OUTER APPLY(
SELECT max(A.VALUE) MAXVALUE
, code
FROM [dbo].[TestTable] A
WHERE DATEPART(HOUR,[DATE]) <= 6 AND DATEPART(MINUTE,[DATE]) < 30
and A.code = tt.code
group by code
)MAXValBefore630
where ( [DATE] > DATEADD(MINUTE,390,CAST({ fn CURDATE()} AS DATETIME) ) ) --6:30 today
group by tt.code
)
SELECT SUM(ResultSubstraction)
FROM RESULT

T-SQL applying pivot for two rows under a single column

On SQL Server 2014 I use the following code:
CREATE TABLE TempTable
(
LType varchar(255),
LStatus varchar(255),
PAmount decimal(16,2),
RAmount decimal(16,2));
INSERT INTO TempTable (LType, LStatus, PAmount, RAmount)
VALUES ('TypeA','StatusA', '1000', '10'),
('TypeB', 'StatusC', '500', '50'),
('TypeC', 'StatusB', '2500', '100'),
('TypeB', 'StatusB', '1000', '50'),
('TypeA', 'StatusA', '3000', '25'),
('TypeC', 'StatusB', '2200', '50');
Select Ltype, Lstatus, SUM(PAmount) as PAmount, SUM(RAmount) as RAmount
From TempTable
Where PAmount > 0
Group By LType, LStatus
to get this table:
What I’m trying to achieve is:
I used pivot but was unable to apply it simultaneously for PAmount and RAmount under Status columns.
Can anyone help with solution?
You can use conditional aggregation for this. This assumes you will always have these values. If you need this to be dynamic then there is a bit more work to do.
select StatusA_PAMount = max(case when Totals.Lstatus = 'StatusA' then Totals.PAmount end)
, StatusA_RAMount = max(case when Totals.Lstatus = 'StatusA' then Totals.RAmount end)
, StatusB_PAMount = max(case when Totals.Lstatus = 'StatusB' then Totals.PAmount end)
, StatusB_RAMount = max(case when Totals.Lstatus = 'StatusB' then Totals.RAmount end)
, StatusC_PAMount = max(case when Totals.Lstatus = 'StatusC' then Totals.PAmount end)
, StatusC_RAMount = max(case when Totals.Lstatus = 'StatusC' then Totals.RAmount end)
from
(
Select Lstatus
, SUM(PAmount) as PAmount
, SUM(RAmount) as RAmount
From TempTable
Where PAmount > 0
Group By LStatus
) Totals

Write a query for this table

Imagine that i have a table like below,i want to write a query that give me below result,is it possible?
Result:
100 , 2015-01-01 , ABC , XYZ
You can use PIVOT.
Query
select userid,
[date],
[job],
[address]
from
(
select userid,name,[value] from tblName
)
as s
pivot
(
max([value]) for [name] in ([date], [job], [address])
) as p;
SQL Fiddle
OR
Query
select userid,
max(case when name = 'date' then [value] else null end) as [date],
max(case when name = 'job' then [value] else null end) as job,
max(case when name = 'address' then [value] else null end) as address
from tblName
group by userid;
SQL Fiddle
Another way,
try using Group_Concatenate and Common Table Expression
;with cte as
(select userid,
STUFF((
SELECT ',' + md.[value]
FROM tblName md
WHERE m.userid = md.userid
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 1, '')
as result
from tblName m group by m.userid
)
select (cast(userid as varchar(50))+','+result) as res from cte
see Demo Here

How to Display data with 'yes' or 'no' entries unique.

This is my data
unitCode stunum assNo subStatus
SIT111 1000 1 Yes
SIT111 1000 2 No
SIT111 3000 1 No
How do i generate only results with ONLY a 'No'
eg: Only student 3000 should come up. Since that person has not handed in ANY assignments. ?
You can use the following statement:
select
*
from
(
select
unitCode,
stunum,
sum(case when subStatus = 'Yes' then 1 else 0 end) as CountYes,
sum(case when subStatus = 'No' then 1 else 0 end) as CountNo
from
students
group by
unitCode, stunum
) as student
inner join student_details
on student.stunum = student_details.stunum
where
CountNo > 0 and CountYes = 0;
declare #T table
(
unitCode varchar(10),
stunum int,
assNo int,
subStatus varchar(3)
)
insert into #T values
('SIT111', 1000, 1, 'Yes'),
('SIT111', 1000, 2, 'No'),
('SIT111', 3000, 1, 'No')
select *
from #T as T
where T.subStatus = 'No' and
T.stunum not in (select stunum
from #T
where subStatus = 'Yes')

Resources