SQL: Showing column name of calculated result - sql

So I am calculating the max of a series of column and I was wondering what the best way to show the name of the result column.
Example Table:
hour1 hour2 hour3
16 10 5
My query looks like this:
(SELECT Max(v)
FROM (VALUES (hour1) , (hour2) , (hour3))
AS VALUE (v)) AS PEAK_VALUE
Note this is in another query.
Desired output:
PEAK_VALUE PEAK_HOUR
16 hour1
I would also like to do further calculations on the PEAK_VALUE column as well. For example dividing it by 2 for this output:
PEAK_VALUE HALF_VALUE PEAK_HOUR
16 8 hour1

You almost got it though you have some issue with your query syntax. You need to add the column name to the un-pivot. After that user row_number() to find the max value
SELECT PEAK_VALUE = v,
HALF_VALUE = v / 2,
PEAK_HOUR = c
FROM
(
SELECT *, rn = row_number() over (order by v desc)
FROM example
CROSS APPLY
(
VALUES ('hour1', hour1) , ('hour2', hour2) , ('hour3', hour3)
) AS VALUE (c, v)
) v
WHERE rn = 1
dbfiddle

Just another option sans row_number()
Example
Select top 1
Peak_Value = Hour
,Half_Value = Hour/2
,Peak_Hour = Item
From YourTable A
Cross Apply (values ('hour1',hour1)
,('hour2',hour2)
,('hour3',hour3)
) B (Item,Hour)
Order by Hour Desc
Results
Peak_Value Half_Value Peak_Hour
16 8 hour1

Another approach using UNPIVOT.
DECLARE #table table(hour1 int, hour2 int, hour3 int)
insert into #table
values(16,10,5)
SELECT top 1 max(val) as peak_value, max(val) /2 as Half_Value, [hour]
FROM #table
unpivot (val for [hour] in ([hour1],[hour2],[hour3])) as t
group by [hour]
order by max(val) desc
peak_value
Half_Value
hour
16
8
hour1

You can shove a whole unpivoting query inside a CROSS APPLY, and then do further calculations:
SELECT t.*, v.*
FROM yourTable t
CROSS APPLY (
SELECT TOP (1)
v.PEAK_VALUE,
HALF_VALUE = v.PEAK_VALUE / 2,
v.PEAK_HOUR,
FROM (VALUES
('hour1', hour1),
('hour2', hour2),
('hour3', hour3)
) AS v(PEAK_HOUR, PEAK_VALUE)
ORDER BY PEAK_VALUE DESC
) AS v
This is slightly different from the other answers, in that it will calculate the subquery per row

Related

how to pivot simple table of 2 rows in postgresql?

expected table is this:
good_days bad_days
6 25
But I have this table:
day_type x
bad_days 25
good_days 6
my code is not working:
select *
from (select * from main_table)t
pivot(count(x) for day_type in ('bad_days', 'good_days') ) as pivot_table
There are multiple ways to do this
Use postgresql extension tablefunc which contains crosstab method which can accept query result and pivot the result
You can also create a custom query (only works if you've less and known day_type column values)
WITH cte (day_type, x) AS (
VALUES ('bad_days', 25), ('good_days', 6))
SELECT sum(good_days) AS good_days,
sum(bad_days) AS bad_days
FROM (
(SELECT x AS good_days,
0 AS bad_days
FROM cte
WHERE day_type = 'good_days')
UNION ALL
(SELECT 0 AS good_days,
x AS bad_days
FROM cte
WHERE day_type = 'bad_days')) AS foo
A simple method is conditional aggregation:
select sum(x) filter (where data_type = 'bad_days') as bad_days,
sum(x) filter (where data_type = 'good_days') as good_days
from t;

Is there a way to use parameters in subquery with groupby and having in sql query?

I am trying to use rank for getting a rank for each alphabet and select that rank in having function using parameters
Ex:
Here is my table data:
Alphabet Date
A 2019-12-19 12:31:43.633
A 2019-12-19 12:31:43.650
B 2019-11-07 11:37:08.560
select *
from (select alphabet, date,
dense_Rank() over (partition by alphabet order by date) RankOrder
from mytable
) A
group by alphabet, date, RankOrder
having RankOrder = 1
By using the above query here is my Result:
Alphabet Date Rank Order
A 2019-12-19 12:31:43.633 1
B 2019-11-07 11:37:08.560 1
What if I had to do this for multiple alphabets using parameters?
using declare #palphabet int='A',#pyear nvarchar(20)=2019
How can I add the parameters to the above query?
You can add where clause :
select a.*
from(select alphabet,date,dense_Rank() over (partition by alphabet order by date) RankOrder
from mytable mt
where mt.Alphabet = #palphabet and year(mt.date) = #pyear
) a
where RankOrder = 1;
I don't think GROUP BY with HAVING clause is required here. As you have a raw data not the aggregate data & you are returning only 1 row for each alphabet by using where RankOrder = 1.
Note : You can't use INT Alphabet as base table contains text value. So, change the type of variable #palphabet.
I am not totally clear what you want but I suspect you are asking you can reduce the rows returned to be only for a specific alphabet for a specific year. Something like this should work for you.
declare #mytable table (Alphabet char(1), MyDate Datetime)
insert #mytable values
('A', '2019-12-19 12:31:43.633')
, ('A', '2019-12-19 12:31:43.650')
, ('B', '2019-11-07 11:37:08.560')
declare #Alphabet char(1) = 'A'
, #Year int = 2019
select *
from
(
select t.Alphabet
, t.MyDate
, RankOrder = dense_Rank() over (partition by t.Alphabet order by t.MyDate)
from #mytable t
--filter the rows here instead of in the final select statement
where t.Alphabet = #Alphabet
and t.MyDate >= convert(char(4), #year) + '0101'
and t.MyDate <= convert(char(4), #year + 1) + '0101'
) A
where A.RankOrder = 1
I want to get the most recent Date and time for the above query that is the reason I user rank to filter the rank one which is the most recent. Also, I used Group by to group my rank according to the alphabet :
Using Group by:
A 2019-12-19 12:31:43.633 1
A 2019-12-19 12:31:43.650 2
A 2019-12-19 12:31:43.667 3
B 2019-11-07 11:37:08.560 1
B 2019-11-07 11:37:08.577 2
Having is allowing me to select only the rank with 1 which is what I need.
A 2019-12-19 12:31:43.633 1
B 2019-11-07 11:37:08.560 1
C 2019-10-30 15:06:36.643 1
D 2019-11-05 16:16:17.920 1
If I had to do the same by using parameters. The problem is my parameter is in this format #date='F2019/2020' and my date format is 2019-12-19 12:31:43.633. How do I select the particular alphabet and the most recent date for that alphabet using a parameter?

Select first 10 rows of a column in a table

Can you please let me know how to select first 10 rows for every cusip in the below table
SELECT [dt_dim_id],
[cusip_num],
sr.[product_dim_id],
[acct_dim_id],
[product_name]
FROM [csr_prod].[dbo].[stock_rec] AS sr,
[csr_prod].[dbo].[product_dim] AS pd
WHERE sr.product_dim_id = pd.product_dim_id
AND dt_dim_id = 20180927
ORDER BY dt_dim_id,
product_dim_id,
acct_dim_id;
Use ROW_NUMBER() with a partition over your groups and order by whatever you need, then filter for the first 10 rows:
;WITH paging AS
(
SELECT ROW_NUMBER() OVER (PARTITION BY cusip_num
ORDER BY dt_dim_id, product_dim_id, acct_dim_id) n,
[dt_dim_id],
[cusip_num],
sr.[product_dim_id],
[acct_dim_id],
[product_name]
FROM [csr_prod].[dbo].[stock_rec] AS sr,
[csr_prod].[dbo].[product_dim] AS pd
WHERE sr.product_dim_id = pd.product_dim_id
AND dt_dim_id = 20180927
)
SELECT * FROM paging WHERE n <= 10
Use apply:
select . . .
from [csr_prod].[dbo].[stock_rec] sr cross apply
(select top (10) pd.*
from [csr_prod].[dbo].[product_dim] pd =
where sr.product_dim_id = pd.product_dim_id and dt_dim_id = 20180927
order by ? -- whatever column defines the first 10 records
) p

Alphanumeric sort on nvarchar(50) column

I am trying to write a query that will return data sorted by an alphanumeric column, Code.
Below is my query:
SELECT *
FROM <<TableName>>
CROSS APPLY (SELECT PATINDEX('[A-Z, a-z][0-9]%', [Code]),
CHARINDEX('', [Code]) ) ca(PatPos, SpacePos)
CROSS APPLY (SELECT CONVERT(INTEGER, CASE WHEN ca.PatPos = 1 THEN
SUBSTRING([Code], 2,ISNULL(NULLIF(ca.SpacePos,0)-2, 8000)) ELSE NULL END),
CASE WHEN ca.PatPos = 1 THEN LEFT([Code],
ISNULL(NULLIF(ca.SpacePos,0)-0,1)) ELSE [Code] END) ca2(OrderBy2, OrderBy1)
WHERE [TypeID] = '1'
OUTPUT:
FFS1
FFS2
...
FFS12
FFS1.1
FFS1.2
...
FFS1.1E
FFS1.1R
...
FFS12.1
FFS12.2
FFS.12.1E
FFS12.1R
FFS12.2E
FFS12.2R
DESIRED OUTPUT:
FFS1
FFS1.1
FFS1.1E
FFS1.1R
....
FFS12
FFS12.1
FFS12.1E
FFS12.1R
What am I missing or overlooking?
EDIT:
Let me try to detail the table contents a little better. There are records for FFS1 - FFS12. Those are broken into X subs, i.e., FFS1.1 - FFS1.X to FFS12.1 - FFS12.X. The E and the R was not a typo, each sub record has two codes associated with it: FFS1.1E & FFS1.1R.
Additionally I tried using ORDER BY but it sorted as
FFS1
...
FFS10
FFS2
This will work for any count of parts separated by dots. The sorting is alphanumerical for each part separately.
DECLARE #YourValues TABLE(ID INT IDENTITY, SomeVal VARCHAR(100));
INSERT INTO #YourValues VALUES
('FFS1')
,('FFS2')
,('FFS12')
,('FFS1.1')
,('FFS1.2')
,('FFS1.1E')
,('FFS1.1R')
,('FFS12.1')
,('FFS12.2')
,('FFS.12.1E')
,('FFS12.1R')
,('FFS12.2E')
,('FFS12.2R');
--The query
WITH Splittable AS
(
SELECT ID
,SomeVal
,CAST(N'<x>' + REPLACE(SomeVal,'.','</x><x>') + N'</x>' AS XML) AS Casted
FROM #YourValues
)
,Parted AS
(
SELECT Splittable.*
,ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS PartNmbr
,A.part.value(N'text()[1]','nvarchar(max)') AS Part
FROM Splittable
CROSS APPLY Splittable.Casted.nodes(N'/x') AS A(part)
)
,AddSortCrit AS
(
SELECT ID
,SomeVal
,(SELECT LEFT(x.Part + REPLICATE(' ',10),10) AS [*]
FROM Parted AS x
WHERE x.ID=Parted.ID
ORDER BY PartNmbr
FOR XML PATH('')
) AS SortColumn
FROM Parted
GROUP BY ID,SomeVal
)
SELECT ID
,SomeVal
FROM AddSortCrit
ORDER BY SortColumn;
The result
ID SomeVal
10 FFS.12.1E
1 FFS1
4 FFS1.1
6 FFS1.1E
7 FFS1.1R
5 FFS1.2
3 FFS12
8 FFS12.1
11 FFS12.1R
9 FFS12.2
12 FFS12.2E
13 FFS12.2R
2 FFS2
Some explanation:
The first CTE will transform your codes to XML, which allows to address each part separately.
The second CTE returns each part toegther with a number.
The third CTE re-concatenates your code, but each part is padded to a length of 10 characters.
The final SELECT uses this new single-string-per-row in the ORDER BY.
Final hint:
This design is bad! You should not store these values in concatenated strings... Store them in separate columns and fiddle them together just for the output/presentation layer. Doing so avoids this rather ugly fiddle...

SQL query for finding first missing sequence string (prefix+no)

T-SQL query for finding first missing sequence string (prefix+no)
Sequence can have a prefix + a continuing no.
ex sequence will be
ID
-------
AUTO_500
AUTO_501
AUTO_502
AUTO_504
AUTO_505
AUTO_506
AUTO_507
AUTO_508
So above the missing sequence is AUTO_503 or if there is no missing sequence then it must return next sequence.
Also starting no is to specified ex. 500 in this case and prefix can be null i.e. no prefix only numbers as sequence.
You could LEFT JOIN the id numbers on shifted(+1) values to find gaps in sequential order:
SELECT
MIN(a.offsetnum) AS first_missing_num
FROM
(
SELECT 500 AS offsetnum
UNION
SELECT CAST(REPLACE(id, 'AUTO_', '') AS INT) + 1
FROM tbl
) a
LEFT JOIN
(SELECT CAST(REPLACE(id, 'AUTO_', '') AS INT) AS idnum FROM tbl) b ON a.offsetnum = b.idnum
WHERE
a.offsetnum >= 500 AND b.idnum IS NULL
SQLFiddle Demo
Using a recursive CTE to dynamically generate the sequence between the min and max of the ID Numbers maybe over complicated things a bit but it seems to work -
LIVE ON FIDDLE
CREATE TABLE tbl (
id VARCHAR(55)
);
INSERT INTO tbl VALUES
('AUTO_500'),
('AUTO_501'),
('AUTO_502'),
('AUTO_504'),
('AUTO_505'),
('AUTO_506'),
('AUTO_507'),
('AUTO_508'),
('509');
;WITH
data_cte(id)AS
(SELECT [id] = CAST(REPLACE(id, 'AUTO_', '') AS INT) FROM tbl)
,maxmin_cte(minId, maxId)AS
(SELECT [minId] = min(id),[maxId] = max(id) FROM data_cte)
,recursive_cte(n) AS
(
SELECT [minId] n from maxmin_cte
UNION ALL
SELECT (1 + n) n FROM recursive_cte WHERE n < (SELECT [maxId] from maxmin_cte)
)
SELECT x.n
FROM
recursive_cte x
LEFT OUTER JOIN data_cte y ON
x.n = y.id
WHERE y.id IS NULL
Check this solution.Here you just need to add identity column.
CREATE TABLE tbl (
id VARCHAR(55),
idn int identity(0,1)
);
INSERT INTO tbl VALUES
('AUTO_500'),
('AUTO_501'),
('AUTO_502'),
('AUTO_504'),
('AUTO_505'),
('AUTO_506'),
('AUTO_507'),
('AUTO_508'),
('509');
SELECT min(idn+500) FROM tbl where 'AUTO_'+cast((idn+500) as varchar)<>id
try this:
with cte as(
select cast(REPLACE(id,'AUTO_','') as int)-500+1 [diff],ROW_NUMBER()
over(order by cast(REPLACE(id,'AUTO_','') as int)) [rnk] from tbl)
select top 1 'AUTO_'+cast(500+rnk as varchar(50)) [ID] from cte
where [diff]=[rnk]
order by rnk desc
SQL FIddle Demo
Had a similar situation, where we have R_Cds that were like this R01005
;with Active_R_CD (R_CD)
As
(
Select Distinct Cast(Replace(R_CD,'R', ' ') as Int)
from table
where stat = 1)
select Arc.R_CD + 1 as 'Gaps in R Code'
from Active_R_CD as Arc
left outer join Active_R_CD as r on ARC.R_CD + 1 = R.R_CD
where R.R_CD is null
order by 1