I have the following table
Date Input Output Rate
10/10/2001 10 15 0.5
10/11/2002 13 14 0.3
1. Date Input Output Rate
2. 10/10/2001 10 15 0.5
3. 10/11/2002 13 14 0.3
I need to transform this table in the following table:
1. Date Category Amount
2. 10/10/2001 Input 10
3. 10/10/2001 Output 15
4. 10/10/2001 Rate 0.5
5. 10/11/2002 Input 13
6. 10/11/2002 Output 14
7. 10/11/2002 Rate 0.3
Which query should I use?
Thank you in advance.
You can use UNPIVOT:
DECLARE #T TABLE (Date DATE, Input DECIMAL(5, 1), [Output] DECIMAL(5, 1), Rate DECIMAL(5, 1));
INSERT #T VALUES ('20011010', 10, 15, 0.5), ('20021110', 13, 14, 0.3);
SELECT upvt.Date, upvt.Category, upvt.Amount
FROM #T
UNPIVOT
( Amount
FOR Category IN ([input], [Output], [Rate])
) upvt;
However, I prefer the slightly more flexible CROSS APPLY with a table valued constructor:
DECLARE #T TABLE (Date DATE, Input DECIMAL(5, 1), [Output] DECIMAL(5, 1), Rate DECIMAL(5, 1));
INSERT #T VALUES ('20011010', 10, 15, 0.5), ('20021110', 13, 14, 0.3);
SELECT t.Date, upvt.Category, upvt.Amount
FROM #T AS t
CROSS APPLY
(VALUES
('Input', t.Input),
('Output', t.[Output]),
('Rate', t.Rate)
) AS upvt (Category, Amount);
As it affords you more flexibility with your datatype conversions, and with renaming your columns slightly differently, e.g. it is easier to make a column like CallStartTime something like Call Start, so it is more report friendly.
e.g if your input and output columns are actually integers, you will get an error message when using UNPIVOT:
The type of column "Rate" conflicts with the type of other columns specified in the UNPIVOT list.
So you would need to use a subquery, and convert your columns there:
DECLARE #T TABLE (Date DATE, Input INT, [Output] INT, Rate DECIMAL(5, 1));
INSERT #T VALUES ('20011010', 10, 15, 0.5), ('20021110', 13, 14, 0.3);
SELECT upvt.Date, upvt.Category, upvt.Amount
FROM ( SELECT Date,
Input = CONVERT(DECIMAL(5, 1), Input),
[Output] = CONVERT(DECIMAL(5, 1), [Output]),
Rate
FROM #T
) AS t
UNPIVOT
( Amount
FOR Category IN ([input], [Output], [Rate])
) upvt;
And while you have to do the same conversion with CROSS APPLY, it seems like less clutter to me:
DECLARE #T TABLE (Date DATE, Input INT, [Output] INT, Rate DECIMAL(5, 1));
INSERT #T VALUES ('20011010', 10, 15, 0.5), ('20021110', 13, 14, 0.3);
SELECT t.Date, upvt.Category, upvt.Amount
FROM #T AS t
CROSS APPLY
(VALUES
('Input', CONVERT(DECIMAL(5, 1), Input)),
('Output', CONVERT(DECIMAL(5, 1), [Output])),
('Rate', t.Rate)
) AS upvt (Category, Amount);
It is of course, personal preference.
You might use a simple Standard SQL UNION ALL which is supported by every DBMS -> portable :-)
SELECT Date, 'Input' AS Category, Cast(Input as DEC(5,1)) AS Amount
FROM Tab
UNION ALL
SELECT Date, 'Output' AS Category, Output
FROM Tab
UNION ALL
SELECT Date, 'Rate' AS Category, Rate
FROM Tab
Related
This question already has answers here:
SQL Server dynamic PIVOT query?
(9 answers)
Closed 1 year ago.
I tried to search before asking, but I didn't find something similar to the one I try to figure out. I use sql server to achieve that.
Current Situation
Target
Based on the Year, I want to pivot:
Col as the name of a new column
value should be the value of the column.
In that example, the first 36 rows should become one row. For every year there should be one row.
A B C D YEAR E F HiBioInsec HiChemInsec etc
76 1 191 4 2020 5000 2000
76 1 191 4 2021 5000 2000
I tried with pivot and max but I didn't got the expected output.
Any thoughts?
PIVOT seems like exactly what you need, actually. Does yours look like this at all? This worked for me.
CREATE TABLE dbo.YourTable
(
A int,
B int,
C int,
D int,
[Year] int,
E int,
F int,
col varchar(30),
[value] int
);
INSERT dbo.YourTable (A,B,C,D,[Year],col, [value])
VALUES (76, 1, 191, 4, 2020, 'HiBioInsec', 5000);
INSERT dbo.YourTable (A,B,C,D,[Year],col, [value])
VALUES (76, 1, 191, 4, 2020, 'HiChemInsec', 2000);
INSERT dbo.YourTable (A,B,C,D,[Year],col, [value])
VALUES (76, 1, 191, 4, 2021, 'HiBioInsec', 5000);
INSERT dbo.YourTable (A,B,C,D,[Year],col, [value])
VALUES (76, 1, 191, 4, 2021, 'HiChemInsec', 2000);
SELECT A,B,C,D,[Year],E,F
, pvt.HiBioInsec
, pvt.HiChemInsec
FROM
(
SELECT A,B,C,D,[Year],E,F,col, SUM([value]) AS SumValue
FROM dbo.YourTable [tbl]
GROUP BY A,B,C,D,[Year],E,F,col
) src
PIVOT (SUM(SumValue) FOR col IN ([HiBioInsec],[HiChemInsec])) pvt
Can you post your SQL?
You could also try something like this, but I don't see how you can get around using an aggregate.
SELECT A,B,C,D,[Year],E,F
, SUM(HiBioInsec) AS HiBioInsec
,SUM(HiChemInsec) AS HiChemInsec
FROM
(
SELECT A,B,C,D,[Year],E,F, [value] AS HibioInsec ,NULL AS HiChemInsec
FROM dbo.YourTable WHERE col = 'HiBioInsec'
UNION ALL
SELECT A,B,C,D,[Year],E,F, NULL AS HibioInsec , [value] AS HiChemInsec
FROM dbo.YourTable WHERE col = 'HiChemInsec'
) tbl
GROUP BY A,B,C,D,[Year],E,F
I have a salary table which has Amount and Amount Precision columns.
Using precision I want to get the actual amount.
Please help me to calculate actual amount using precision.
use POWER(). The multiplication with 1.0 is required to convert your Amount in integer into decimal
ActualAmount = Amount * 1.0 / Power(10, AmountPrecision)
You can use the following query:
select CAST(Amount as double precision) / power(10, AmountPrecision) from AmountTest
Assuming AmountTest is the name of the table. You can replace it with the name given by you.
DECLARE #T TABLE (
Amount INT,
AmountPrecision INT
)
INSERT INTO #T VALUES(1,1),(51,1),(51,2),(934,3),(1024,2)
SELECT
*,
CAST(AMOUNT AS FLOAT)/(CONCAT(1,REPLICATE(0,AMOUNTPRECISION))) AS ACTUALAMOUNT
FROM #T
You can also try the divide by just creating value using REPLICATE() function.
CREATE TABLE Data (id int, AmountPrecision int)
INSERT INTO Data (id, AmountPrecision) VALUES
(1, 1)
, (51, 1)
, (51, 2)
, (934, 3)
, (1024, 2)
Select id
, Cast('1' + REPLICATE('0', AmountPrecision) as int) as DivideBy
, Cast(id * 1.0 / Cast('1' + REPLICATE('0', AmountPrecision) as int)
as float) as FormattedNumber
from Data
Live db<>fiddle demo.
Scenario: I have a table with Year and Gap columns. What I need the output as, starting from the given year value it incremented up to the value in the gap column.
i.e., If the YearVal is 2001, and Gap is 3, I need the output as
Result
--------
2001
2002
2003
What I have tried:
DECLARE #ResultYears TABLE (Gap INT, YearVal INT);
INSERT INTO #ResultYears (Gap, YearVal) VALUES (3, 2001);
;WITH FinalResult AS (
SELECT YearVal AS [YR] FROM #ResultYears
UNION ALL
SELECT [YR] + 1 FROM FinalResult
WHERE [YR] + 1 <= (SELECT YearVal + (Gap -1) FROM #ResultYears)
)
SELECT * FROM FinalResult;
db<>fiddle demo with one entry in the table.
Using the query above, I can achieve the expected result. But if the table have more than one entry, the query is not working.
i.e., If I have the entries in the table as below:
DECLARE #ResultYears TABLE (Gap INT, YearVal INT);
INSERT INTO #ResultYears (Gap, YearVal) VALUES
(3, 2001), (4, 2008), (1, 2014), (2, 2018);
How can I modify the query to achieve my expected result?
db<>fiddle demo with more than one entry in the table.
Is this what you're after?
DECLARE #ResultYears TABLE (Gap INT, YearVal INT);
INSERT INTO #ResultYears (Gap, YearVal) VALUES
(3, 2001), (4, 2008), (1, 2014), (2, 2018);
WITH N AS(
SELECT N
FROM (VALUES(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL))N(N)),
Tally AS(
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) - 1 AS I
FROM N N1, N N2), --100 is more than enough
Years AS(
SELECT RY.YearVal + T.I AS [Year],
RY.Gap,
RY.YearVal
FROM #ResultYears RY
JOIN Tally T ON RY.Gap > T.I)
SELECT *
FROM Years Y
ORDER BY Y.YearVal;
Personally I prefer a tally table over a rCTE; they are far quicker, especially with large datasets, or where the rCTE would have to do a high volume of recursion.
Demo on db<>fiddle
Initially Create one user defined table type function which return the Gap years
CREATE FUNCTION [dbo].[ufn_GetYears]
(
#i_Gap INT,#Year INT
)
RETURNS #Temp TABLE
(
Years INT
)
AS
BEGIN
;WITH CTE
AS
(
SELECT 1 AS Seq,DATEFROMPARTS ( #Year,01,01) AS Years
UNION ALL
SELECT seq +1,DATEADD(YEAR,1,Years)
FROM Cte
WHERE Seq < #i_Gap
)
INSERT INTO #Temp
SELECT DATEPART(YEAR,Years )
FROM CTE
RETURN
END
Sample Data
DECLARE #ResultYears TABLE
(Gap INT,
YearVal INT
);
INSERT INTO #ResultYears (Gap, YearVal) VALUES
(3, 2001), (4, 2008), (1, 2014), (2, 2018);
Sql Query to get the expected result using CROSS APPLY
SELECT R.Gap,dt.Years
FROM #ResultYears R
CROSS APPLY [dbo].[ufn_GetYears](R.Gap,R.YearVal) AS dt
Result
Gap Years
---------
3 2001
3 2002
3 2003
4 2008
4 2009
4 2010
4 2011
1 2014
2 2018
2 2019
If for a reason, you prefer recursive CTE (which is definetly slower)
DECLARE #ResultYears TABLE (Gap INT, YearVal INT);
INSERT INTO #ResultYears (Gap, YearVal) VALUES (3, 2001), (4, 2008), (1, 2014), (2, 2018);
;WITH FinalResult AS (
SELECT YearVal, Gap, YearVal [YR] FROM #ResultYears
UNION ALL
SELECT YearVal, Gap, [YR] + 1
FROM FinalResult
WHERE [YR] + 1 <= YearVal + (Gap -1)
)
SELECT * FROM FinalResult
ORDER BY [YR];
You need to keep original row parameters in the recursive part. this way recursion runs as desired.
I need to pivot a table as show below using column "channel" and grouping it based on Units.
Actual table:
The result I need is shown below
I'm not an expert with pivotting and unpivoting concepts, I'm trying the below query to achieve the above result
SELECT [service_point_ID]
,isnull([1],0) - isnull([2],0) as net_usage_value
,[units]
,[1]
,[2]
,[channel_ID]
,[date]
,[time]
,[is_estimate]
,[UTC_offset]
,[import_history_id]
FROM #temp1
AS SourceTable PIVOT(sum(usage_value) FOR channel IN([1],[2])) AS PivotTable
If I execute this query I'm getting the below result
The same logic is achieved in r -Refernce link Pivot using Mutiple columns
Here is the SQL fiddle for this one
CREATE TABLE #temp1
(
Service_point_ID varchar(10) NUll,
usage_value decimal(18,6) NULL,
units varchar(10) NUll,
[date] Date NULL,
[time] time NULL,
channel varchar(2) NULL,
[Channel_ID] varchar(2) NULL,
is_estimate varchar(2) NULL,
UTC_Offset varchar(20) NULL
)
INSERT INTO #temp1 VALUES ('123',1.000000,'kvarh','2017-01-01','0015','1','11','A','-500')
INSERT INTO #temp1 VALUES ('123',0.200000,'kvarh','2017-01-01','0015','2','11','A','-500')
INSERT INTO #temp1 VALUES ('123',0.200000,'kwh','2017-01-01','0015','1','11','A','-500')
INSERT INTO #temp1 VALUES ('123',0.400000,'kwh','2017-01-01','0015','2','11','A','-500')
Any help is much appreciated.
This is solution using pivot function:
declare #table table(
service_point_id int,
usage_value float,
units varchar(10),
[date] date,
[time] char(4),
channel int,
channel_id int,
is_estimate char(1),
utc_offset int,
import_history int,
datecreated datetime
)
--example data you provided
insert into #table values
(123, 1, 'kvarh', '2017-01-01', '0015', 1, 11, 'A', -500, 317, '2018-03-20 10:32:42.817'),
(123, 0.2, 'kwh', '2017-01-01', '0015', 1, 33, 'A', -500, 317, '2018-03-20 10:32:42.817'),
(123, 0.3, 'kvarh', '2017-01-01', '0015', 2, 11, 'A', -500, 317, '2018-03-20 10:32:42.817'),
(123, 0.4, 'kwh', '2017-01-01', '0015', 2, 33, 'A', -500, 317, '2018-03-20 10:32:42.817')
--pivot query that does the work, it's only matter of aggregation one column, as mentioned already, so pivot query is really simple and concise
select *, [1]-[2] [net_usage_value] from
(select * from #table) [t]
pivot (
max(usage_value)
for channel in ([1],[2])
) [a]
SELECT [service_point_ID]
sum(,isnull([1],0) - isnull([2],0)) as net_usage_value
,[units]
,sum(isnull([1],0))[1]
,sum(isnull([2],0))[2]
,[channel_ID]
,[date]
,[time]
,[is_estimate]
,[UTC_offset]
,[import_history_id]
FROM #temp1
AS SourceTable PIVOT(sum(usage_value) FOR channel IN([1],[2])) AS PivotTable
group by [service_point_ID], [units],[channel_ID]
,[date]
,[time]
,[is_estimate]
,[UTC_offset]
,[import_history_id]
Inner join will out perform the pivot syntax. SQL Server pivot vs. multiple join
select a.usage_value - b.usage_value as net_usage_value , other columns
from #temp1 a inner join #temp1 b on a.service_point_id = b.service_point_id
and a.units = b.units
and a.channel = 1
and b.channel = 2
gets around the group by as well.
I am stuck on converting a varchar column schedule containing the following data 0, 1, 2, 3,4,8,9,10,11,12,15,16,17,18,19 to INT. I know, please don't ask why this schedule column was not created as INT initially, long story.
So I tried this, but it doesn't work. and give me an error:
select CAST(schedule AS int) from shift_test:
the query should check if the numbers representing days are found in the schedule filed using the sql code below
select empid, case when ((DateDiff(hour,'01-01-2014 07:00' , '01-01-2014 00:00')/ 24 )% 15) in ( CAST(schedule AS int))
then 'A' else '*' end as shift_A from Shift_test
After executing i get this error.
Conversion failed when converting the varchar value to int.
Any help will be appriciated
Use ISNUMERIC() test if you are using version 2008 or 2008R2. In SQL SERVER 2012 you can use TRY_CAST() function, which checks if the data conversion is allowed for given literal.
Mock up code for SQL Server 2008/R2:
Select col1, col2,
case
when <condition> and isnumeric(col2) then cast(col2 as int)
else <do whatever...>
end
as converted_col2
from <yourtable>;
For SQL Server 2012:
Select col1, col2,
case
when <condition> then try_cast(col2 as int)
else <do whatever...>
end
as converted_col2
from <yourtable>;
Example with SQl Server 2008
declare #T table (empid int, schedule varchar(2)) ;
insert into #T(empid, schedule) values (1, '1');
insert into #T(empid, schedule) values (2, '2');
insert into #T(empid, schedule) values (3, '03');
insert into #T(empid, schedule) values (4, '4');
insert into #T(empid, schedule) values (5, '05');
insert into #T(empid, schedule) values (6, 'A');
select empid,
case
when ISNUMERIC(schedule) = 1
and ((DateDiff(hour,'01-01-2014 07:00' , '10-01-2014 00:00')/ 24 )% 15)
in ( CAST(schedule AS int)) then 'A'
else '*'
end
as shift_A
from #T;
# Binaya Ive added my code for more help.
The schedule column contain (0, 1, 2, 3,4,8,9,10,11,12,15,16,17,18,19) which is varchar.
i want to output A if after this calculation ((DateDiff(hour,'01-01-2014 07:00',startdate)/ 24 )% 15) the result is found in the schedule column else if after the calculation and the result is not found output *
;with Shift_runover (shift_code,schedule,endd,startdate)
-- Start at the beginning of shift.
as
(select shift_code,schedule,Cast(end_date as DateTime) as endd,Cast(start_date as DateTime)as startdate from dbo.Shift_test
union all
-- Add hours up to the desired end date.
select shift_code,schedule,endd,DateAdd(hour, 1,startdate)from Shift_runover where startdate<=endd),
Extendedsamples as
(
-- Calculate the number of days since the beginning of the first shift on 1/1/2014.
select shift_code,schedule,startdate,DateDiff(hour,'01-01-2014 07:00',startdate)/ 24 as Days from Shift_runover ),
Shifts as
(
-- the schedule column contain (0, 1, 2, 3,4,8,9,10,11,12,15,16,17,18,19) which is varchar.
-- i want to output A if ((DateDiff(hour,'01-01-2014 07:00',startdate)/ 24 )% 15) is found in the schedule colume
select *,
case when (DateDiff(hour,'01-01-2014 07:00',startdate)/ 24 )% 15 in(schedule)
then 'A' else '*' end as shift_A
from ExtendedSamples
)
select *
from Shifts
option ( maxrecursion 0 )