Dynamically Insert into Table A Based on Row_Num from Table B - sql

I've condensed some data into TableB which looks like the following:
+========+==========+=========+
| AreaID | AreaName | Row_Num |
+========+==========+=========+
| 506 | BC-VanW | 1 |
+--------+----------+---------+
| 3899 | BC-VicS | 2 |
+--------+----------+---------+
| 1253 | AB-CalW | 3 |
+--------+----------+---------+
There are 2000 unique rows in total, Row_Num from 1 to 2000, and every AreaID in this table is naturally unique as well.
I now want to insert into a blank table, TableA, which has the following columns:
+========+==========+=========+============+===========+
| AreaID | StartDT | EndDT | MarketCode |Allocation |
+========+==========+=========+============+===========+
The insert statement I want to use is repeats everything except for the AreaID
I was attempting some things earlier and this is a basic look at what I have that I'm hoping Stackoverflow could help me expand on:
DECLARE #AreaID NVARCHAR(4)
SET #AreaID = (SELECT AreaID FROM TableB WHERE Row_Num = 1)
DECLARE #Sql NVARCHAR(MAX)
SET #Sql = N'
INSERT INTO TableA (AreaID, StartDt, EndDt, MarketCode, Allocation) VALUES ('+#AreaID+', ''2020-11-01 00:00:00.000'', ''2049-12-31 00:00:00.000'' , 31 , 25.00);
INSERT INTO TableA (AreaID, StartDt, EndDt, MarketCode, Allocation) VALUES ('+#AreaID+', ''2020-11-01 00:00:00.000'', ''2049-12-31 00:00:00.000'' , 38 , 60.00);
INSERT INTO TableA (AreaID, StartDt, EndDt, MarketCode, Allocation) VALUES ('+#AreaID+', ''2020-11-01 00:00:00.000'', ''2049-12-31 00:00:00.000'' , 39 , 15.00);
'
EXEC sp_executesql #Sql
GO
From here I would want it to 'loop' through the Row_Nums, once and once only, and run the full insert query above eventually doing it for all 2000 Row_Nums.
Of course, if there is a more efficient way please let me know and I will take a look.
Thanks!

I think you want a cross join with a fixed list of values:
INSERT INTO TableA (AreaID, StartDt, EndDt, MarketCode, Allocation)
SELECT b.AreaID, x.*
FROM tableB b
CROSS APPLY (VALUES
('2020-11-01 00:00:00.000', '2049-12-31 00:00:00.000', 31, 25.00),
('2020-11-01 00:00:00.000', '2049-12-31 00:00:00.000', 38, 60.00),
('2020-11-01 00:00:00.000', '2049-12-31 00:00:00.000', 39, 15.00)
) x(StartDt, EndDt, MarketCode, Allocation)
If the date range is always the same, this can be simplified a little:
INSERT INTO TableA (AreaID, StartDt, EndDt, MarketCode, Allocation)
SELECT b.AreaID, '2020-11-01 00:00:00.000', '2049-12-31 00:00:00.000', x.*
FROM tableB b
CROSS APPLY (VALUES
(31 , 25.00),
(38 , 60.00),
(39 , 15.00)
) x(MarketCode, Allocation)

Related

SQL Query for fetching 2 results as single row

I have table like this. I want to get employee records to get their current Designation(whose effectiveto is null) and the date where they FIRST joined as Trainee(min(effectivefrom) where Designation= Trainee)
+----+-------------------+---------------+-------------+
| ID | Designation | EffectiveFrom | EffectiveTo |
+----+-------------------+---------------+-------------+
| 1 | Trainee | 01/01/2000 | 31/12/2000 |
| 1 | Assistant Manager | 01/01/2001 | 31/12/2004 |
| 1 | Suspended | 01/01/2005 | 01/02/2005 |
| 1 | Trainee | 02/03/2005 | 31/03/2005 |
| 1 | Manager | 01/04/2005 | NULL |
| 2 | Trainee | 01/01/2014 | 31/12/2014 |
| 2 | Developer | 01/01/2015 | 31/12/2016 |
| 2 | Architect | 01/01/2017 | NULL |
+----+-------------------+---------------+-------------+
How to get result like this
+----+---------------------+---------------------+
| ID | Current Designation | Date First Employed |
+----+---------------------+---------------------+
| 1 | Manager | 01/01/2000 |
| 2 | Architect | 01/01/2014 |
+----+---------------------+---------------------+
The date of first employment could be located using CROSS APPLY and SELECT TOP(1)
CREATE TABLE #table1(
ID int,
Designation varchar(17),
EffectiveFrom datetime,
EffectiveTo varchar(10));
INSERT INTO #table1
(ID, Designation, EffectiveFrom, EffectiveTo)
VALUES
(1, 'Trainee', '2000-01-01 01:00:00', '31/12/2000'),
(1, 'Assistant Manager', '2001-01-01 01:00:00', '31/12/2004'),
(1, 'Suspended', '2005-01-01 01:00:00', '01/02/2005'),
(1, 'Trainee', '2005-02-03 01:00:00', '31/03/2005'),
(1, 'Manager', '2005-01-04 01:00:00', NULL),
(2, 'Trainee', '2014-01-01 01:00:00', '31/12/2014'),
(2, 'Developer', '2015-01-01 01:00:00', '31/12/2016'),
(2, 'Architect', '2017-01-01 01:00:00', NULL);
select t.ID, t.Designation [Current Designation],
ef.EffectiveFrom [Date First Employed]
from #table1 t
cross apply (select top(1) cast(tt.EffectiveFrom as date) EffectiveFrom
from #table1 tt
where t.ID=tt.ID
and Designation='Trainee'
order by tt.EffectiveFrom) ef
where t.EffectiveTo is null;
ID Current Designation Date First Employed
1 Manager 2000-01-01
2 Architect 2014-01-01
One method is conditional aggregation. It is a bit unclear how you define "current", but assuming this is associated with EffectiveTo being NULL:
select id,
max(case when EffectiveTo is null then designation end) as current_designation,
min(effectivefrom) as start_ate
from t
group by id;
You can try below query:
select id,max(current_designation) current_designation,min(date_first_employee) date_first_employee from
(select id,
max(case when EffectiveTo is null then designation end) over (partition by id) as current_designation,
(case when Designation='Trainee' then EffectiveFrom end) Date_First_Employee
from desig) t
group by id
Output:
This is another possilbe solution
SQL Fiddle
MySQL 5.6 Schema Setup:
CREATE TABLE table1
(`ID` int, `Designation` varchar(17), `EffectiveFrom` datetime, `EffectiveTo` varchar(10))
;
INSERT INTO table1
(`ID`, `Designation`, `EffectiveFrom`, `EffectiveTo`)
VALUES
(1, 'Trainee', '2000-01-01 01:00:00', '31/12/2000'),
(1, 'Assistant Manager', '2001-01-01 01:00:00', '31/12/2004'),
(1, 'Suspended', '2005-01-01 01:00:00', '01/02/2005'),
(1, 'Trainee', '2005-02-03 01:00:00', '31/03/2005'),
(1, 'Manager', '2005-01-04 01:00:00', NULL),
(2, 'Trainee', '2014-01-01 01:00:00', '31/12/2014'),
(2, 'Developer', '2015-01-01 01:00:00', '31/12/2016'),
(2, 'Architect', '2017-01-01 01:00:00', NULL)
;
Query 1:
SELECT
ID,
(SELECT
`Designation`
FROM
table1
WHERE
`EffectiveFrom` = (SELECT
MAX(`EffectiveFrom`)
FROM
table1
WHERE
ID = t1.ID)) AS `Current Designation`
,DATE(MIN(`EffectiveFrom`)) AS `Date First Employed`
FROM
table1 t1
GROUP BY ID
Results:
| ID | Current Designation | Date First Employed |
|----|---------------------|---------------------|
| 1 | Trainee | 2000-01-01 |
| 2 | Architect | 2014-01-01 |
It's actually a rather simple self-join assuming that the EffectiveFrom and EffectiveTo columns are always filled in appropriately (i.e. there's always only one NULL value for EffectiveTo of a given ID). Since it's possible for someone to be a Trainee twice, you also need to use a window function like ROW_NUMBER() to filter out only the earliest Traininee EffectiveFrom date:
WITH CTE_Designations AS
(
SELECT T1.ID, T1.Designation AS CurrentDesignation, ISNULL(T2.EffectiveFrom, T1.EffectiveFrom) AS DateFirstEmployed -- If the join fails below then that means the earliest Designation is in T1 (e.g. that is the 'Trainee' record)
FROM DesignationsTable AS T1
LEFT JOIN DesignationsTable AS T2
ON T1.ID = T2.ID
AND T1.Designation <> T2.Designation
AND T2.Designation = 'Trainee'
WHERE T1.EffectiveTo IS NULL
),
CTE_Designations_FirstEmployedOnly AS
(
SELECT ID, CurrentDesignation, DateFirstEmployed, ROW_NUMBER() OVER (PARTITION BY ID ORDER BY DateFirstEmployed) AS SortId -- Generates a unique ID per DateFirstEmployed row for each Designation.ID sorted by DateFirstEmployed
FROM CTE_Designations
)
SELECT ID, CurrentDesignation, DateFirstEmployed
FROM CTE_Designations_FirstEmployedOnly
WHERE SortId = 1
Results per your example data:
Results if you had an additional person who was still a Trainee:
This returns the result shown for the given data
Solves for "current Designation(whose effectiveto is null) and the date where they FIRST joined". Also handles terminated employees where EffectiveTo is not NULL as well as new employees with a single row. Replace "#t" with your table name.
SELECT a.Id, d.Designation, a.EffectiveFrom
FROM
(SELECT *, ROW_NUMBER() OVER ( PARTITION BY Id ORDER BY EffectiveFrom ASC ) r FROM #t) a
INNER JOIN
(SELECT *, ROW_NUMBER() OVER ( PARTITION BY Id ORDER BY EffectiveFrom DESC ) r FROM #t) d
ON a.Id = d.Id
WHERE a.r = 1 AND d.r = 1
Result:
Id Designation EffectiveFrom
1 Manager 2000-01-01
2 Architect 2014-01-01

get sales records totaling more than $1000 in any 3 hour timespan?

I'm asking because I'm not sure what to google for - attempts that seemed obvious to me returned nothing useful.
I have sales coming into the database of objects at particular datetimes with particular $ values. I want to get all groups of sales records a) within a (any, not just say "on the hour" like 1am-4am) 3 hour time frame, that b) total >= $1000.
The table looks like:
Sales
SaleId int primary key
Item varchar
SaleAmount money
SaleDate datetime
Even just a suggestion on what I should be googling for would be appreciated lol!
EDIT:
Ok after trying the cross apply solution - it's close but not quite there. To illustrate, consider the following sample data:
-- table & data script
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[Sales](
[pkid] [int] IDENTITY(1,1) NOT NULL,
[item] [int] NULL,
[amount] [money] NULL,
[saledate] [datetime] NULL,
CONSTRAINT [PK_Sales] PRIMARY KEY CLUSTERED
(
[pkid] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
INSERT [dbo].[Sales] VALUES (1, 649.3800, CAST(N'2017-12-31T21:46:19.000' AS DateTime))
INSERT [dbo].[Sales] VALUES (1, 830.6700, CAST(N'2018-01-01T08:38:58.000' AS DateTime))
INSERT [dbo].[Sales] VALUES (1, 321.0400, CAST(N'2018-01-01T09:08:04.000' AS DateTime))
INSERT [dbo].[Sales] VALUES (3, 762.0300, CAST(N'2018-01-01T07:26:30.000' AS DateTime))
INSERT [dbo].[Sales] VALUES (2, 733.5100, CAST(N'2017-12-31T12:04:07.000' AS DateTime))
INSERT [dbo].[Sales] VALUES (3, 854.5700, CAST(N'2018-01-01T08:32:11.000' AS DateTime))
INSERT [dbo].[Sales] VALUES (2, 644.1700, CAST(N'2017-12-31T17:49:59.000' AS DateTime))
INSERT [dbo].[Sales] VALUES (1, 304.7700, CAST(N'2018-01-01T08:01:50.000' AS DateTime))
INSERT [dbo].[Sales] VALUES (2, 415.1200, CAST(N'2017-12-31T20:27:28.000' AS DateTime))
INSERT [dbo].[Sales] VALUES (3, 698.1700, CAST(N'2018-01-01T02:39:28.000' AS DateTime))
A simple adaptation of the cross apply solution from the comments, to go item by item:
select s.*
, s2.saleamount_sum
from Sales s cross apply
(select sum(s_in.amount) as saleamount_sum
from Sales s_in
where s.item = s_in.item
and s.saledate >= s_in.saledate and s_in.saledate < dateadd(hour, 3, s.saledate)
) s2
where s2.saleamount_sum > 1000
order by s.item, s.saledate
So the actual data (sorted by item/time) looks like:
pkid item amount saledate
1 1 649.38 2017-12-31 21:46:19.000
8 1 304.77 2018-01-01 08:01:50.000
2 1 830.67 2018-01-01 08:38:58.000
3 1 321.04 2018-01-01 09:08:04.000
5 2 733.51 2017-12-31 12:04:07.000
7 2 644.17 2017-12-31 17:49:59.000
9 2 415.12 2017-12-31 20:27:28.000
10 3 698.17 2018-01-01 02:39:28.000
4 3 762.03 2018-01-01 07:26:30.000
6 3 854.57 2018-01-01 08:32:11.000
and the result of the cross apply method:
pkid item amount saledate saleamount_sum
2 1 830.67 1/1/18 8:38 AM 1784.82
3 1 321.04 1/1/18 9:08 AM 2105.86
7 2 644.17 12/31/17 5:49 PM 1377.68
9 2 415.12 12/31/17 8:27 PM 1792.8
4 3 762.03 1/1/18 7:26 AM 1460.2
6 3 854.57 1/1/18 8:32 AM 2314.77
The issue can be seen by considering the method's analysis of Item 1. From the data, we see that FIRST sale of item 1 does not participate in a 3-hour-over-$1000. The second, third, and fourth Item 1 sales however do so participate. And they are correctly picked out, pkid = 2 and 3. But their sums aren't right - both of their sums include the very FIRST sale of Item 1, which does not participate in the timespan/amount condition. I would have expected the saleamount_sum for pkid 2 to be 1135.44, and for pkid 3 to be 1456.48 (their reported sums minus the first non-participating sale).
Hopefully that makes sense. I'll try fiddling with the cross apply query to get it. Anyone who can quickly see how to get what I'm after, please feel free to chime in.
thanks,
-sff
Here is one method using apply:
select t.*, tt.saleamount_sum
from t cross apply
(select sum(t2.saleamount) as saleamount_sum
from t t2
where t2.saledate >= t.saledate and t2.saledate < dateadd(hour, 3, t.saledate)
) tt
where tt.saleamount_sum > 1000;
Edit:
If you want this per item (which is not specified in the question), then you need a condition to that effect:
select t.*, tt.saleamount_sum
from t cross apply
(select sum(t2.saleamount) as saleamount_sum
from t t2
where t2.item = t.item and t2.saledate >= t.saledate and t2.saledate < dateadd(hour, 3, t.saledate)
) tt
where tt.saleamount_sum > 1000;
Your query had one wrong comparison (s.saledate >= s_in.saledate) instead of s_in.saledate >= s.saledate. The inner query below looks for the next 3 hours for each row of the outer query.
Sample data
DECLARE #Sales TABLE (
[pkid] [int] IDENTITY(1,1) NOT NULL,
[item] [int] NULL,
[amount] [money] NULL,
[saledate] [datetime] NULL
);
INSERT INTO #Sales VALUES (1, 649.3800, CAST(N'2017-12-31T21:46:19.000' AS DateTime))
INSERT INTO #Sales VALUES (1, 830.6700, CAST(N'2018-01-01T08:38:58.000' AS DateTime))
INSERT INTO #Sales VALUES (1, 321.0400, CAST(N'2018-01-01T09:08:04.000' AS DateTime))
INSERT INTO #Sales VALUES (3, 762.0300, CAST(N'2018-01-01T07:26:30.000' AS DateTime))
INSERT INTO #Sales VALUES (2, 733.5100, CAST(N'2017-12-31T12:04:07.000' AS DateTime))
INSERT INTO #Sales VALUES (3, 854.5700, CAST(N'2018-01-01T08:32:11.000' AS DateTime))
INSERT INTO #Sales VALUES (2, 644.1700, CAST(N'2017-12-31T17:49:59.000' AS DateTime))
INSERT INTO #Sales VALUES (1, 304.7700, CAST(N'2018-01-01T08:01:50.000' AS DateTime))
INSERT INTO #Sales VALUES (2, 415.1200, CAST(N'2017-12-31T20:27:28.000' AS DateTime))
INSERT INTO #Sales VALUES (3, 698.1700, CAST(N'2018-01-01T02:39:28.000' AS DateTime))
INSERT INTO #Sales VALUES (4, 600, CAST(N'2018-01-01T02:39:01.000' AS DateTime))
INSERT INTO #Sales VALUES (4, 600, CAST(N'2018-01-01T02:39:02.000' AS DateTime))
INSERT INTO #Sales VALUES (4, 600, CAST(N'2018-01-01T02:39:03.000' AS DateTime))
INSERT INTO #Sales VALUES (4, 600, CAST(N'2018-01-01T02:39:04.000' AS DateTime))
INSERT INTO #Sales VALUES (4, 600, CAST(N'2018-01-01T02:39:05.000' AS DateTime))
INSERT INTO #Sales VALUES (4, 600, CAST(N'2018-01-01T02:39:06.000' AS DateTime))
Query
select
s.*
, s2.saleamount_sum
from
#Sales AS s
cross apply
(
select sum(s_in.amount) as saleamount_sum
from #Sales AS s_in
where
s.item = s_in.item
and s_in.saledate >= s.saledate
and s_in.saledate < dateadd(hour, 3, s.saledate)
) AS s2
where s2.saleamount_sum > 1000
order by s.item, s.saledate
;
Result
+------+------+--------+-------------------------+----------------+
| pkid | item | amount | saledate | saleamount_sum |
+------+------+--------+-------------------------+----------------+
| 8 | 1 | 304.77 | 2018-01-01 08:01:50.000 | 1456.48 |
| 2 | 1 | 830.67 | 2018-01-01 08:38:58.000 | 1151.71 |
| 7 | 2 | 644.17 | 2017-12-31 17:49:59.000 | 1059.29 |
| 4 | 3 | 762.03 | 2018-01-01 07:26:30.000 | 1616.60 |
| 11 | 4 | 600.00 | 2018-01-01 02:39:01.000 | 3600.00 |
| 12 | 4 | 600.00 | 2018-01-01 02:39:02.000 | 3000.00 |
| 13 | 4 | 600.00 | 2018-01-01 02:39:03.000 | 2400.00 |
| 14 | 4 | 600.00 | 2018-01-01 02:39:04.000 | 1800.00 |
| 15 | 4 | 600.00 | 2018-01-01 02:39:05.000 | 1200.00 |
+------+------+--------+-------------------------+----------------+
I added 6 rows with item=4 to the sample data. They are all within 3 hours and there are 5 subsets of these 6 rows that have a sum larger than 1000. Technically this result is correct, but do you really want this kind of result?
To get all sales within a specified hours interval:
SELECT SaleId, sum(SaleAmount) as amount FROM Sales WHERE (HOUR(SaleDate) BETWEEN 1 AND 4) GROUP BY SaleId HAVING amount >=1000;
You can add other conditions in WHERE clause.
If you're looking for periods like 0:00-3:00, 3:00-6:00, you can group by those intervals. The following query rounds the hour to multiples of three, combines it with the date, and groups on that:
select format(dt, 'yyyy-mm-dd') + ' ' +
cast(datepart(hour, dt) / 3 * 3 as varchar(2)) as period
, sum(amount) as total
from sales
group by
format(dt, 'yyyy-mm-dd') + ' ' +
cast(datepart(hour, dt) / 3 * 3 as varchar(2))
having sum(amount) > 1000
Working example at regtester.
If you're looking for any 3 hour period, like 0:33-3:33 or 12:01-15:01, see Gordon Linoff's answer.

Selecting the second (middle) row between MIN & MAX values in SQL Server

I have the following table:
TicketNumber CallDate
--------------------------------------------
101 10/09/2015 3:15:43 PM
101 10/09/2015 3:45:43 PM
101 11/19/2015 2:23:09 PM
I want to select the min date, the middle date and the max date. It is easy to get the first and last dates using MIN and MAX. But how to SELECT (get) the second date?
SELECT
TicketNumber
, MIN(CallDate) CallDate1
, MAX(CallDate) CallDate3
, COUNT(TicketNumber) [Count]
FROM Table1
WHERE -(conditions)-
GROUP BY TicketNumber
HAVING COUNT(TicketNumber)=3
Between MIN & MAX dates in the SELECT statement I want the second row date.
The expected output should be:
TicketNumber CallDate1 CallDate2 CallDate3 Count
------------------------------------------------------------------------------------------
101 10/9/2015 3:15:43 PM 10/9/2015 3:45:43 PM 11/19/2015 2:23:09 PM 3
Here is one possible variant. At first number and count all rows, then filter only those TicketNumbers that have three tickets and PIVOT result.
SQL Fiddle
Sample data
DECLARE #Tickets TABLE (TicketNumber int, CallDate datetime2(0));
INSERT INTO #Tickets (TicketNumber, CallDate) VALUES
(101, '2015-10-09 03:15:43'),
(101, '2015-10-09 03:45:43'),
(101, '2015-11-19 02:23:09'),
(102, '2015-11-20 02:23:09'),
(102, '2015-11-19 02:23:09'),
(102, '2015-11-21 02:23:09'),
(103, '2015-11-10 02:23:09'),
(103, '2015-11-19 02:23:09'),
(104, '2015-11-11 02:23:09'),
(104, '2015-11-01 02:23:09'),
(104, '2015-11-21 02:23:09'),
(104, '2015-11-30 02:23:09');
Query
WITH
CTE
AS
(
SELECT
TicketNumber
,CallDate
,ROW_NUMBER() OVER (PARTITION BY TicketNumber ORDER BY CallDate) AS rn
,COUNT(*) OVER (PARTITION BY TicketNumber) AS cnt
FROM
#Tickets AS T
)
SELECT
P.TicketNumber
,[1] AS CallDate1
,[2] AS CallDate2
,[3] AS CallDate3
,cnt
FROM
CTE
PIVOT (MIN(CTE.CallDate) FOR rn IN ([1], [2], [3])) AS P
WHERE cnt = 3
ORDER BY P.TicketNumber;
Result
+--------------+---------------------+---------------------+---------------------+-----+
| TicketNumber | CallDate1 | CallDate2 | CallDate3 | cnt |
+--------------+---------------------+---------------------+---------------------+-----+
| 101 | 2015-10-09 03:15:43 | 2015-10-09 03:45:43 | 2015-11-19 02:23:09 | 3 |
| 102 | 2015-11-19 02:23:09 | 2015-11-20 02:23:09 | 2015-11-21 02:23:09 | 3 |
+--------------+---------------------+---------------------+---------------------+-----+
This can be achieved using table JOINS.
SELECT t1.TicketNumber, t2.CallDate1, t1.CallDate AS CallDate2, t2.CallDate3, t2.Count
FROM tickets AS t1
JOIN (
SELECT TicketNumber, MIN(CallDate) AS CallDate1, MAX(CallDate) AS CallDate3,
COUNT(TicketNumber) AS Count
FROM tickets
GROUP BY TicketNumber
HAVING COUNT(TicketNumber)=3
) AS t2
ON t1.TicketNumber = t2.TicketNumber
WHERE t1.CallDate > t2.CallDate1
AND t1.CallDate < t2.CallDate3
Working Fiddle

SQL Pivot data by time

I'm trying to get a pivot of my data, based on the timestamp. I want to group them into half-hour "buckets". For example, with the data below:
CREATE TABLE #test (
Employee nvarchar(20) NOT NULL
,[SaleTime] time NOT NULL
,Amount float NOT NULL
)
INSERT INTO #test VALUES
('A', '08:10', '100.50')
,('A', '12:20', '758.23')
,('A', '11:59', '592.11')
,('B', '12:00', '95.00')
,('B', '09:01', '29.10')
,('B', '09:04', '53.22')
,('C', '11:23', '55.77')
,('C', '10:40', '128.00')
I would like the result to be something like
Time | A | B | C |
-----------------------------------------------------------------
08:00 - 08:30 | 100.5 | | |
08:30 - 09:00 | | | |
09:00 - 09:30 | | 82.32 | |
09:30 - 10:00 | | | |
10:00 - 10:30 | | | |
10:30 - 11:00 | | | 128.00 |
11:00 - 11:30 | | | 55.77 |
11:30 - 12:00 | 592.11 | | |
12:00 - 12:30 | 758.23 | 95.00 | |
12:30 - 13:00 | | | |
-----------------------------------------------------------------
Do I have to create an empty table with the timeslots in order to do this? Is there a method of doing this without using CASE WHEN?
Thanks!
Maybe I'm try to solve a non-existent problem but I'd like to offer an alternative solution which is dynamic in respect to the number of employees (it adds columns for extra employees) and sums the amounts sold in a time slot per employee if there's more than one sale in that slot; if you don't aggregate the amount you'll end up with multiple rows for every time slot where someone has sold more than one time.
The time slot generation is borrowed from the excellent solution in Nenads answer.
First the the test data with a couple of extra rows to illustrate the difference:
DROP TABLE #test;
CREATE TABLE #test (
Employee nvarchar(20) NOT NULL
,[SaleTime] time NOT NULL
,Amount float NOT NULL
)
INSERT INTO #test VALUES
('A', '08:10', '100.50')
,('A', '12:20', '758.23')
,('A', '11:59', '592.11')
,('B', '12:00', '95.00')
,('B', '09:01', '29.10')
,('B', '09:04', '53.22')
,('C', '11:23', '55.77')
,('C', '10:40', '128.00')
,('D', '09:40', '28.00')
,('E', '11:40', '50.00')
,('E', '11:35', '20.00')
The query build a SQL statement dynamically and executes it with the EXECUTE statement:
DECLARE #Headers VARCHAR(MAX)
SELECT #Headers = COALESCE(#Headers + ',[' + Employee + ']', '[' + Employee + ']')
FROM (SELECT DISTINCT Employee FROM #test) Emp
DECLARE #SQL NVARCHAR(MAX)
SET #SQL = N'
WITH CTE_TimeSlots AS
(
SELECT CAST(''8:00'' AS TIME) AS StartTime, CAST(''8:30'' AS TIME) AS EndTime
UNION ALL
SELECT DATEADD(MI, 30, StartTime), DATEADD(MI, 30, EndTime)
FROM CTE_TimeSlots
WHERE StartTime <= ''12:00''
)
SELECT * FROM (
SELECT CONVERT(NVARCHAR(10),StartTime) + '' - '' + CONVERT(NVARCHAR(10),EndTime) AS [Time], Amount, Employee
FROM CTE_TimeSlots t
LEFT JOIN #test d ON d.SaleTime >= StartTime AND d.SaleTime < EndTime
) innerQuery
PIVOT (SUM(Amount) FOR Employee IN (' + #Headers + ')
) AS PivotTable
'
--PRINT #SQL -- Uncomment to see the query which will be run
EXECUTE(#SQL)
You can use recursive CTE to create your time slots 'on the fly' and then join it to your data. There is a way to avoid using CASE, you can use PIVOT command instead, but I think this is much simpler:
WITH CTE_TimeSlots AS
(
SELECT CAST('8:00' AS TIME) AS StartTime, CAST('8:30' AS TIME) AS EndTime
UNION ALL
SELECT DATEADD(MI, 30, StartTime), DATEADD(MI, 30, EndTime)
FROM CTE_TimeSlots
WHERE StartTime <= '12:00'
)
SELECT CONVERT(NVARCHAR(10),StartTime) + ' - ' + CONVERT(NVARCHAR(10),EndTime) AS [Time]
, CASE WHEN Employee = 'A' THEN Amount END AS A
, CASE WHEN Employee = 'B' THEN Amount END AS B
, CASE WHEN Employee = 'C' THEN Amount END AS C
FROM CTE_TimeSlots t
LEFT JOIN #test d ON d.SaleTime >= StartTime AND d.SaleTime < EndTime
SQLFiddle DEMO

Get row from date interval even if it doesnt exist from one table

I have this query
SELECT Date, IFNULL(Price, '------') AS Price
FROM productHistory
WHERE Date between '2012-08-15' and '2012-08-19' AND Company='AAA' AND Product='PPP'
GROUP BY Date
and the result is:
Date.......Price
-------------------
2012-08-15...100,00
2012-08-19...110,00
and it should be like this:
Date.........Price
-------------------------------------
2012-08-15......100,00
2012-08-16......--------
2012-08-17......--------
2012-08-18......--------
2012-08-19......110,00
I am working with only one table, I have checked similar quiestions in this forum but I could not find a solution.
When I get this, what I want to do is to add more Companies as columns in the query to get a result like this. well for this I have to change the query..
Date.........PriceCompany1.....PriceCompany2.....PriceCompany3
----------------------------------------------------------
2012-08-15......100,00................................100,00..................................100,0
2012-08-16......---------...............................100,00...................................---------
2012-08-17......---------..............................----------.................................110,00
2012-08-19......110,00..............................100,00..................................----------
What do I need to do?
Create StoredProcedure for that
CREATE PROCEDURE [dbo].[GetMasterData]
#startDate DATETIME,
#endDate DATETIME
AS
BEGIN
SET NOCOUNT ON;
WITH dates(Date) AS
(
SELECT #startdate as Date
UNION ALL
SELECT DATEADD(d,1,[Date])
FROM dates
WHERE DATE < #enddate
)
SELECT Date as Date1 into #tmp1 FROM dates
OPTION (MAXRECURSION 0)
Select Date1,IsNULL(Price, '------') AS Price from #tmp1 left outer join
productHistory on #tmp1.Date1 = productHistory.Date
and Company='AAA' AND Product='PPP'
Drop table #tmp1
END
and execute sp using below line
exec GetMasterData '08/15/2012','08/19/2012'
No need for procedures, just plain SQL will do:
-- generate "pseudo calendar table" from the data:
WITH dt AS (
SELECT DISTINCT zdate FROM producthistory
WHERE zdate between '2012-08-10' and '2012-08-20'
)
-- a poor man's PIVOT:
SELECT dt.zdate
, pa.price AS price_a
, pb.price AS price_b
, pc.price AS price_c
FROM dt
LEFT JOIN producthistory pa ON pa.zdate = dt.zdate AND pa.company='AAA' AND pa.product='ppp'
LEFT JOIN producthistory pb ON pb.zdate = dt.zdate AND pb.company='BBB' AND pb.product='ppp'
LEFT JOIN producthistory pc ON pc.zdate = dt.zdate AND pc.company='CCC' AND pc.product='ppp'
ORDER BY zdate
;
Since the OP does not show any table definition or data, I'll have to invent my own:
CREATE TABLE producthistory
( company varchar
, product varchar
, zdate Date NOT NULL
, price INTEGER
);
INSERT INTO producthistory ( company, product, zdate, price ) VALUES
( 'AAA', 'ppp', '2012-08-15', 100)
, ( 'AAA', 'ppp', '2012-08-15', 110)
, ( 'AAA', 'ppp', '2012-08-15', 120)
, ( 'BBB', 'ppp', '2012-08-16', 200)
, ( 'BBB', 'qqq', '2012-08-16', 210)
, ( 'BBB', 'ppp', '2012-08-16', 220)
, ( 'CCC', 'ppp', '2012-08-15', 300)
;
RESULTS:
CREATE TABLE
INSERT 0 7
zdate | price_a | price_b | price_c
------------+---------+---------+---------
2012-08-15 | 100 | | 300
2012-08-15 | 110 | | 300
2012-08-15 | 120 | | 300
2012-08-16 | | 200 |
2012-08-16 | | 220 |
(5 rows)
adding the IFNULL/COALESCE function (or other formatting trivia) is left as an exercise to the reader