SQL SERVER - CHANGE COLUMN NAME TO VALUE - sql-server

I have a table like this:
ID | Date 1 | Date 2 | Date 3
1 | 2014-08-01 | 2014-08-02 | 2014-08-03
And I need to output it like this:
ID | Date Field Name | Date Value
1 | Date 1 | 2014-08-01
1 | Date 2 | 2014-08-02
1 | Date 3 | 2014-08-03
Have tried dynamic unpivoting with unions but seems messy. Is there a best practice way of doing this?

I think UNPIVOT is the best practice here. I don't find it messy so much as confusing, maybe because I don't reach for it that often. This will give the results you're looking for:
SELECT ID, [Date Field Name], [Date Value]
FROM myTable
UNPIVOT ([Date Value] FOR [Date Field Name] IN ([date 1], [date 2], [date 3])) AS x

Related

UNNEST array and assign to new columns with CASE WHEN

I have following BigQuery table, which has nested structure, i.e. example below is one record in my table.
Id | Date | Time | Code
AQ5ME | 120520 | 0950 | 123
---------- | 150520 | 1530 | 456
My goal is to unnest the array to achieve the following structure (given that 123 is the Start Date code and 456 is End Date code):
Id | Start Date | Start Time | End Date | End Time
AQ5ME | 120520 | 0950 | 150520 | 1530
I tried basic UNNEST in BigQuery and my results are as follows:
Id | Start Date | Start Time | End Date | End Time
AQ5ME | 120520 | 0950 | NULL | NULL
AQ5ME | NULL | NULL | 150520 | 1530
Could you please support me how to unnest it in a correct way as described above?
You can calculate mins and max within the row, and extract them as a new column.
Since you didn't show the full schema, I assume Date and Time are separate arrays.
For that case, you can use that query:
SELECT Id,
(SELECT MIN(D) from UNNEST(Date) as d) as StartDate,
(SELECT MIN(t) from UNNEST(Time) as t) as StartTime,
(SELECT MAX(D) from UNNEST(Date) as d) as EndDate,
(SELECT MAX(t) from UNNEST(Time) as t) as EndTime
FROM table
As in Sabri's response - using aggregation functions while unnesting works perfectly. To use this fields later on for sorting purposes (in ORDER BY statement) SAFE_OFFSET[0] can be used, like for example below:
...
ORDER BY StartDate[SAFE_OFFSET(0)] ASC

Update a column with LastExclusionDate

In SQL Server 2012, I have a table t1 where we store a list of excluded product.
I would like to add a column LastExclusionDate to store the date since when the product has been excluded.
Every day the product is inserted into the table if it is excluded. If not there will be no row and the next time when the product will be excluded there will be a gap date with the previous insert.
I would like to find a T-SQL query to update the LastExclusionDate column.
I would like to use it to populate column LastExclusionDate the first time (=initialisation) and use it every day to update the column when we insert a new row
I've tried this query, but I don't know how to get LastExclusionDate!
;WITH Cte AS
(
SELECT
product_id,
CreationDate,
LAG(CreationDate) OVER (PARTITION BY Product_ID ORDER BY CreationDate) AS GapStart,
(DATEDIFF(DAY, LAG(CreationDate) OVER (PARTITION BY Product_id ORDER BY CreationDate), CreationDate) -1) AS GapDays
FROM
#t1
)
SELECT *
FROM cte
Here's some sample data:
+------------+--------------+--------------------------------+
| product_id | CreationDate | LastExclusionDate_(toPopulate) |
+------------+--------------+--------------------------------+
| 100 | 2018-05-01 | 2018-05-01 |
| 100 | 2018-05-02 | 2018-05-01 |
| 100 | 2018-05-03 | 2018-05-01 |
| 100 | 2018-06-01 | 2018-06-01 |
| 100 | 2018-06-02 | 2018-06-01 |
| 200 | 2018-09-01 | 2018-09-01 |
| 200 | 2018-09-02 | 2018-09-01 |
| 200 | 2018-09-17 | 2018-09-17 |
+------------+--------------+--------------------------------+
Thanks
The idea in finding gap-less sequences is to compare the series to a gap-less sequence and find groups of records where the difference of both doesn't change. For example, when the date increases one by one and a row number also does, then the difference between both stays the same and we found a group:
WITH
cte (product_id, CreationDate, grp) AS (
SELECT product_id, CreationDate
, DATEDIFF(day, '19000101', CreationDate)
- ROW_NUMBER() OVER (PARTITION BY product_id ORDER BY CreationDate)
FROM #t1
)
SELECT product_id, CreationDate
, MIN(CreationDate) OVER (PARTITION BY product_id, grp) AS LastExclusionDate
FROM cte
For ongoing daily insertions it can be done with something like this.
INSERT INTO <yourTable>
SELECT
newProduct.[product_id],
newProduct.[creationDate],
isnull(existingProduct.[lastExclusionDate], newProduct.[creationDate]) AS [lastExclusionDate]
FROM
(SELECT <#product_id> AS [product_id], <#createionDate> AS [creationDate]) AS newProduct
LEFT JOIN #temp existingProduct
ON existingProduct.[product_id] = newProduct.product_id
AND existingProduct.[creationDate] = DATEADD(DAY,-1,newProduct.[creationDate])
I've got a demo here http://rextester.com/BDEO23118 . It's a larger than necessary demo because it uses the code above with the data you provided to populate a table row-by-row like you might in a daily update process. It then does individual insertions using this code with some new dates so you can see the way it handles new ranges. (just an FYI, rextester displays result dates in day.month.year hh:mm:ss format, but you can dump the script into management studio and it will output in DATE format)

Sql Server - Invalid query because of no aggregate function or group by clause for comparing difference between 2 dates

Given a table:
-------------------------------------
| Id | DateField_A | DateField_B |
-------------------------------------
| 1 | 2017-01-01 | 2018-06-01 |
-------------------------------------
| 2 | 2018-02-01 | 2018-06-01 |
-------------------------------------
| 3 | 2018-04-01 | 2018-04-03 |
-------------------------------------
I am trying to write a query that takes the sum of rows between a date range where two date fields are less than 5 days apart.
SELECT Count(*) FROM TABLE
WHERE
DateField_A >= '2018-01-01'
AND DateField_A < '2019-01-01'
HAVING (DateField_B - DateField_A) <= 5
This would return count 1 given the table above.
However I'm getting the following error:
How can I fix the query?
You can only use having and select on fields that are included in a group by clause or included in an aggregate function such as count. In your case, you just want to move that to your where criteria:
SELECT Count(*)
FROM TABLE
WHERE DateField_A >= '2018-01-01'
AND DateField_A < '2019-01-01'
AND (DateField_B - DateField_A) <= 5
As commented below, please be careful when using subtraction or addition for comparing dates. While it might work for this instance, you would be safer using date comparison functions such as dateadd, or in this case, datediff:
...
AND datediff(day, datefield_a, datefield_b) <= 5

How to define a field & group by within Select Query in SQL

At present have a column with status types, and a separate column with dates. What I would like to do is create new columns for each status type. Have created column names by using case when statements, but cannot then group by those.
At present, the table kicks out the following:
Reference | Status | Date
----- | ----------- | -----
1 | Approve | 1/1/2017
1 | In Progress | 1/2/2017
2 | Approve | 1/1/2017
2 | In Progress | 1/2/2017
2 | Close | 1/3/2017
Would like to take this and make:
Reference | Approve | In Progress | Close
--------- | -------- | ----------- | -----
1 | 1/1/2017 | 1/2/2017 |
2 | 1/1/2017 | 1/2/2017 | 1/3/2017
Have a lot of other selects, and intention is to export to excel/run automatically, so trying to avoid temp tables.
I don't know that case when is appropriate, but am struggling to find a better solution.
You can use pivot in sql server
select * from #yourStatus
pivot( max(date) for [Status] in ([Approve], [In Progress],[Close])) p
Your table:
create table #yourStatus (Reference int, Status varchar(20), Date date)
insert into #yourstatus (
Reference , Status , Date ) values
( 1 ,'Approve','1/1/2017')
,( 1 ,'In Progress','1/2/2017')
,( 2 ,'Approve','1/1/2017')
,( 2 ,'In Progress','1/2/2017')
,( 2 ,'Close','1/3/2017')
Something like this should work based on the table you've provided (I've also handled null values to get you the output you need):-
select
Reference,
isnull(convert(varchar(10),[Approve],103),'') [Approve],
isnull(convert(varchar(10),[In Progress],103),'') [In Progress],
isnull(convert(varchar(10),[Close],103),'') [Close]
from
(
select
Reference,
Status,
Date
from YourTableName
) d
pivot (min([Date]) for Status in ([Approve],[In Progress],[Close])) p

SQL Query for Date Range, multiple start/end times

A table exists in Microsoft SQL Server with record ID, Start Date, End Date and Quantity.
The idea is that for each record, the quantity/total days in range = daily quantity.
Given that a table containing all possible dates exists, how can I generate a result set in SQL Server to look like the following example?
EX:
RecordID | Start Date | End Date | Quantity
1 | 1/1/2010 | 1/5/2010 | 30000
2 | 1/3/2010 | 1/9/2010 | 20000
3 | 1/1/2010 | 1/7/2010 | 10000
Results as
1 | 1/1/2010 | QTY (I can do the math easy, just need the dates view)
1 | 1/2/2010 |
1 | 1/3/2010 |
1 | 1/4/2010 |
1 | 1/3/2010 |
2 | 1/4/2010 |
2 | 1/5/2010 |
2 | 1/6/2010 |
2 | 1/7/2010 |
2 | 1/8/2010 |
2 | 1/9/2010 |
3 | 1/1/2010 |
3 | 1/2/2010 |
3 | 1/3/2010 |
3 | 1/4/2010 |
3 | 1/5/2010 |
3 | 1/6/2010 |
3 | 1/7/2010 |
Grouping on dates I could get then get the sum of quantity on that day however the final result set can't be aggregate due to user provided filters that may exclude some of these records down the road.
EDIT
To clarify, this is just a sample. The filters are irrelevant as I can join to the side to pull in details related to the record ID in the results.
The real data contains N records which increases weekly, the dates are never the same. There could be 2000 records with different start and end dates... That is what I want to generate a view for. I can right join onto the data to do the rest of what I need
I should also mention this is for past, present and future data. I would love to get rid of a temporary table of dates. I was using a recursive query to get all dates that exist within a 50 year span but this exceeds MAXRECURSION limits for a view, that I cannot use.
Answer
select RecordId,d.[Date], Qty/ COUNT(*) OVER (PARTITION BY RecordId) AS Qty
from EX join Dates d on d.Date between [Start Date] and [End Date]
ORDER BY RecordId,[Date]
NB: The below demo CTEs use the date datatype which is SQL Server 2008 the general approach should work for SQL2005 as well though.
Test Case
/*CTEs for testing purposes only*/
WITH EX AS
(
SELECT 1 AS RecordId,
cast('1/1/2010' as date) as [Start Date],
cast('1/5/2010' as date) as [End Date],
30000 AS Qty
union all
SELECT 2 AS RecordId,
cast('1/3/2010' as date) as [Start Date],
cast('1/9/2010' as date) as [End Date],
20000 AS Qty
),Dates AS /*Dates Table now adjusted to do greater range*/
(
SELECT DATEADD(day,s1.number + 2048*s2.number,'1990-01-01') AS [Date]
FROM master.dbo.spt_values s1 CROSS JOIN master.dbo.spt_values s2
where s1.type='P' AND s2.type='P' and s2.number <= 8
order by [Date]
)
select RecordId,d.[Date], Qty/ COUNT(*) OVER (PARTITION BY RecordId) AS Qty
from EX join Dates d on d.Date between [Start Date] and [End Date]
ORDER BY RecordId,[Date]
Results
RecordId Date Qty
----------- ---------- -----------
1 2010-01-01 6000
1 2010-01-02 6000
1 2010-01-03 6000
1 2010-01-04 6000
1 2010-01-05 6000
2 2010-01-03 2857
2 2010-01-04 2857
2 2010-01-05 2857
2 2010-01-06 2857
2 2010-01-07 2857
2 2010-01-08 2857
2 2010-01-09 2857
I think you can try this.
SELECT [Quantities].[RecordID], [Dates].[Date], SUM([Quantity])
FROM [Dates]
JOIN [Quantities] on [Dates].[Date] between [Quantities].[Start Date] and [End Date]
GROUP BY [Quantities].[RecordID], [Dates].[Date]
ORDER BY [Quantities].[RecordID], [Dates].[Date]

Resources