Stuck on Unpivot / Pivot Combo SQL Server 2008 - sql-server

I can't seem to get my head around this problem where I think I need a combination of pivot and unpivot in SQL Server 2008:
I have a table as follows:
Sale | Month | Count | Budgeted | Actual
------------------------------------------------
NewSale | 1 | 120 | 45.23 | 50.10
NewSale | 2 | 30 | 3.10 | 1.2
NewSale | 3 | 70 | 45.00 | 100.32
I need to pivot so that the months are as columns, but unpivot so I get the Count, Budgeted, Actual as rows, so it is like so...
Type | 1 | 2 | 3
-----------------------------------
Count | 120 | 30 | 70
Budgeted | 45.23 | 3.10 | 45.00
Actual | 50.10 | 1.2 | 100.32
I've tried this so far, but I can't work out how to put the pivot in there:
select
*
from YTD
pivot
(
sum([Count]), sum([Budgeted]), sum([Actual])
for [Month] in ([1],[2],[3],[4],[5],[6],[7],[8],[9],[10],[11],[12])
)
as figures
This gives me a syntax error as you can't have more than one calculation in the pivot (as far as what I understood from the error.
Help!!!

declare #T table
(
Sale varchar(10),
[Month] int,
[Count] int,
Budgeted money,
Actual money
)
insert into #T values
('NewSale', 1, 120, 45.23, 50.10),
('NewSale', 2, 30, 3.10, 1.2),
('NewSale', 3, 70, 45.00, 100.32)
select [Type], [1], [2], [3]
from
(
select [Month],
cast([Count] as money) as [Count],
Budgeted,
Actual
from #T
) as T
unpivot
(
Value for [Type] in ([Count], Budgeted, Actual)
) as U
pivot
(
sum(Value) for [Month] in ([1], [2], [3])
) as P
Try on SE-Data.

You need to do 3 selects and union the result to get a table that looks like that.
For example
Select 'Count' as [Type], 120 AS 1, 40 AS 2, 70 AS 3
UNION ALL
Select 'Budgeted' as [Type], 45.23 AS 1, 3.10 AS 2, 45.00 AS 3
UNION ALL
Select 'Actual' as [Type], 50.1 AS 1, 1.2 AS 2, 100.32 AS 3
Will look like your example
So just replace each of those selects with a piviot that returns the row you want and you GTG

Related

PIVOT Multiple columns in tsql

I have a table that is constructed like this
custid|prodid|calls|orders|upsell
34 | 2 | 4 | 2 | 1
However i need to Pivot or reconstruct to table to reflect something like
custid|prodid|code |Value
34 | 2 | call | 4
34 | 2 | order | 2
34 | 2 | upsell| 1
You could use a union approach:
SELECT custid, prodid, 'call' AS code, calls AS [Value] FROM yourTable UNION
SELECT custid, prodid, 'order', orders FROM yourTable UNION
SELECT custid, prodid, 'upsell', upsell FROM yourTable;

Filter datetime columns in SQL Server

I have a datetime column that has a 5 min interval between the next data, however I want to see if that column contains any time interval less than 5 mins, particularly 5 secs.
So for example:
one date would read 2018-05-04 19:21:46.000
the next row would read 2018-05-04 19:26:46.000
and 2018-05-04 19:31:46.000.
However, we sometimes get rows that read:
2018-05-04 19:36:46.000
then 2018-05-04 19:36:51.000
then 2018-05-04 19:36:56.000
What SQL script would be best to filter the column to distinguish the erroneous data (the 5 secs interval) from the correct data (5 min interval) especially in a table with thousands of rows?
Hi #Andrea, thanks for that. I have a couple of questions. What does the 'q' stand for? and when i rewrite the query as
SELECT ProductID, MyTimestamp, DATEDIFF(second, xMyTimestamp, MyTimestamp) as DIFFERENCE_IN_SECONDS
FROM (
SELECT *,
Lag(MyTimestamp) OVER (ORDER BY MyTimestamp, ProductID) as xMyTimestamp
FROM TableName
) q
WHERE xMyTimestamp IS NOT NULL and ProductID= 31928
I get this result which doesn't compute the time accurately.
+-----------+-------------------------+-----------------------+
| ProductID | MyTimestamp | DIFFERENCE_IN_SECONDS |
+-----------+-------------------------+-----------------------+
| 31928 | 2017-03-21 13:36:30.000 | 0 |
| 31928 | 2017-03-21 13:46:30.000 | 0 |
| 31928 | 2017-03-21 13:56:32.000 | 0 |
| 31928 | 2017-03-21 14:01:32.000 | 0 |
| 31928 | 2017-03-21 14:11:32.000 | 0 |
| 31928 | 2017-03-21 14:16:32.000 | 0 |
| 31928 | 2017-03-21 14:26:32.000 | 0 |
| 31928 | 2017-03-21 14:36:32.000 | 0 |
+-----------+-------------------------+-----------------------+
Any reason why
Since you are on 2014, you can use LEAD to compare the value of one row, to the value of the next.
declare #table table(id int identity(1,1), interval datetime)
insert into #table
values
('2018-05-04 19:21:46.000'),
('2018-05-04 19:26:46.000'),
('2018-05-04 19:31:46.000'),
('2018-05-04 19:36:46.000'),
('2018-05-04 19:36:51.000'),
('2018-05-04 19:36:56.000')
select
id
,interval
,issue_with_row = case
when
isnull(datediff(minute,interval,lead(interval) over (order by id, interval)),0) < 5
then 1
else 0
end
from #table
order by id
Or if you wanted to only see those,
;with cte as(
select
id
,interval
,issue_with_row = case
when
isnull(datediff(minute,interval,lead(interval) over (order by id, interval)),0) < 5
then 1
else 0
end
from #table)
select *
from cte
where issue_with_row = 1
You can use LAG:
declare #tmp table(MyTimestamp datetime)
insert into #tmp values
('2018-05-04 19:21:46.000')
,('2018-05-04 19:26:46.000')
,('2018-05-04 19:31:46.000')
,('2018-05-04 19:36:46.000')
,('2018-05-04 19:36:51.000')
,('2018-05-04 19:36:56.000')
SELECT DATEDIFF(second, xMyTimestamp, MyTimestamp) as DIFFERENCE_IN_SECONDS
FROM (
SELECT *,
LAG(MyTimestamp) OVER (ORDER BY MyTimestamp) xMyTimestamp
FROM #tmp
) q
WHERE xMyTimestamp IS NOT NULL
results:
So you should use it like this:
SELECT DATEDIFF(second, xMyTimestamp, MyTimestamp) as DIFFERENCE_IN_SECONDS
FROM (
SELECT *,
LAG(MyTimestamp) OVER (ORDER BY MyTimestamp) xMyTimestamp
FROM [YOUR_TABLE_NAME_HERE]
) q
WHERE xMyTimestamp IS NOT NULL
Edit
Here is another sample based on new data posted by OP:
declare #tmp table(ProductID int, MyTimestamp datetime)
insert into #tmp values
(31928, '2017-03-21 13:36:30.000')
,(31928, '2017-03-21 13:46:30.000')
,(31928, '2017-03-21 13:56:32.000')
,(31928, '2017-03-21 14:01:32.000')
,(31928, '2017-03-21 14:11:32.000')
,(31928, '2017-03-21 14:16:32.000')
,(31928, '2017-03-21 14:26:32.000')
,(31928, '2017-03-21 14:36:32.000')
SELECT ProductID
,MyTimestamp
,DATEDIFF(second, xMyTimestamp, MyTimestamp) AS DIFFERENCE_IN_SECONDS
FROM (
SELECT *
,Lag(MyTimestamp) OVER (
ORDER BY MyTimestamp
,ProductID
) AS xMyTimestamp
FROM #tmp
) q
WHERE xMyTimestamp IS NOT NULL
AND ProductID = 31928
Output:
Here you can check that the results are calculated correctly.

How can I take the sum of only the max values?

I need to take the max cost of each tracking number (TN) and then sum those values grouped by the OrderNo.
Here's a table:
+----+-----+-------+
|TNo |cost| OrderNo|
+----+-----+-------+
| 1 | 5 | 12 |
| 1 | 4 | 12 |
| 2 | 6 | 12 |
| 2 | 3 | 12 |
| 3 | 3 | 15 |
| 4 | 2 | 15 |
| 4 | 3 | 15 |
+----+-----+-------+
Here's what I want my results to be:
+--------+-----+
| OrderNo| Sum |
+--------+-----+
| 12 | 11 | (6+5)
| 15 | 6 | (3+3)
+--------+-----+
This is what I have so far, but this sums the max but for all instances of the Tracking No. For example, in the above table, for Order# 12, it would sum 5+5+6+6. I only want to sum the max values (5+6).
SELECT ol.OrderNo, SUM(t.maxCost)
FROM (
SELECT
ol.TrackingNumber, MAX(ol.Cost) maxCost
FROM OzLink ol GROUP BY ol.TrackingNumber) t
JOIN OzLink ol ON ol.TrackingNumber=t.TrackingNumber
GROUP BY ol.OrderNo
**Also, I'm new to this work and asking questions on stackoverflow so feedback on how I asked this question would be appreciated!
you could do it like this:
SELECT ol.OrderNo, SUM(ol.maxCost)
FROM (
SELECT
ol.TrackingNumber, MAX(ol.Cost) maxCost, ol.OrderNo
FROM OzLink ol GROUP BY ol.TrackingNumber,ol.OrderNo) ol
GROUP BY ol.OrderNo
You can benefit from cte like below:
CREATE TABLE mytab
(
TNo INT,
Cost INT,
OrderNo INT
)
insert into mytab values (1,5,12)
insert into mytab values (1,4,12)
insert into mytab values (2,6,12)
insert into mytab values (2,3,12)
insert into mytab values (3,3,13)
insert into mytab values (4,2,13)
insert into mytab values (4,3,13)
;with cte (TNo,OrderNo,maxcost) as (
select TNo,OrderNo,Max(Cost) as maxcost
from mytab
group by TNo, OrderNo
)
select OrderNo,SUM(maxcost)
from cte
group by OrderNo
There is a few ways, like the answers below. But you can also use the below query, and create a Row number based on OrderNo and TN and Order by the Cost DESC in the Subquery and then only return the highest cost.
SELECT OrderNo,
SUM(Cost) As Cost
FROM
(
SELECT ROW_NUMBER() OVER(PARTITION BY OrderNo, TN ORDER BY Cost DESC) AS HighestCost,
Cost,
OrderNo,
TN
FROM TableName
) AS Data
WHERE HighestCost = 1
GROUP BY OrderNo
Same as another answer
declare #T TABLE (TNo INT, Cost INT, OrderNo INT);
insert into #T values (1,5,12), (1,4,12), (2,6,12), (2,3,12), (3,3,15), (4,2,15), (4,3,15);
select t.OrderNo, sum(t.cost)
from ( select OrderNo, cost
, ROW_NUMBER() over (partition by TNo, OrderNo order by cost desc) as rn
from #T
) t
where t.rn = 1
group by t.OrderNo;
OrderNo
----------- -----------
12 11
15 6

(T-SQL) How to query an audit table, and find changes between 2 dates

The audit table looks like this:
Audit ID VendorID PaymentType CreateDateUTC
999 8048 2 2017-10-30-08:84:24
1000 1234 5 2017-10-31-01:17:34
1001 8048 7 2017-10-31-01:17:45
1002 1234 5 2017-10-31-01:17:53
1003 1234 7 2017-10-31-01:18:23
1004 1234 5 2017-11-01-01:18:45
In this example, you can see that say - VendorID 1234 started as PaymentType 5, then had another entry where it's still 5 (the audit table records additional changes not relevant to my query), then it changes to 7, but then back to 5.
Say I'd want to answer the question: 'Between now and date X, these VendorIDs had a change in PaymentType'. A bonus would be - this was the previous PaymentType.
Expected Results:
VendorID PaymentType Prev_PaymentType
8048 7 2
So say if I queried between now and 10-31-01:00:00, I'd want it to return VendorID 8048 as having changed (and as a bonus, that it's previous PaymentType was 2), but VendorID 1234 shouldn't show up, since at 2017-10-31-01:00:00 it was a 5, and now is still a 5, despite the intermittent changes.
How would one go about querying the VendorIDs whose payment type changed between 2 dates?
Thanks!
Here is an alternative approach that my prove useful, using OUTER APPLY. Note that the AuditID column is used as a tie-breaker mostly because the sample data does not have datetime values.
SQL Fiddle
CREATE TABLE AuditTable (
AuditID int
, VendorID int
, PaymentType int
, CreateDateUTC date
);
INSERT INTO AuditTable
VALUES (999, 8048, 2, '2017-10-30'),
(1000, 1234, 5, '2017-10-31'),
(1001, 8048, 7, '2017-10-31'),
(1002, 1234, 5, '2017-10-31'),
(1003, 1234, 7, '2017-10-31'),
(1004, 1234, 5, '2017-11-01');
Query 1:
select
*
from AuditTable a
outer apply (
select top(1) PaymentType, CreateDateUTC
from AuditTable t
where a.VendorID = t.VendorID
and a.CreateDateUTC >= t.CreateDateUTC
and a.AuditID > t.AuditID
order by CreateDateUTC DESC, AuditID DESC
) oa (PrevPaymentType, PrevDate)
order by
vendorid
, CreateDateUTC
Results:
| AuditID | VendorID | PaymentType | CreateDateUTC | PrevPaymentType | PrevDate |
|---------|----------|-------------|---------------|-----------------|------------|
| 1000 | 1234 | 5 | 2017-10-31 | (null) | (null) |
| 1002 | 1234 | 5 | 2017-10-31 | 5 | 2017-10-31 |
| 1003 | 1234 | 7 | 2017-10-31 | 5 | 2017-10-31 |
| 1004 | 1234 | 5 | 2017-11-01 | 7 | 2017-10-31 |
| 999 | 8048 | 2 | 2017-10-30 | (null) | (null) |
| 1001 | 8048 | 7 | 2017-10-31 | 2 | 2017-10-30 |
CREATE TABLE AuditTable (
AuditID INT,
VendorID INT,
PaymentType INT,
CreateDateUTC DATE
);
INSERT INTO AuditTable VALUES
(999 , 8048, 2, '2017-10-30'),
(1000, 1234, 5, '2017-10-31'),
(1001, 8048, 7, '2017-10-31'),
(1002, 1234, 5, '2017-10-31'),
(1003, 1234, 7, '2017-10-31'),
(1004, 1234, 5, '2017-11-01');
WITH CTE AS (
SELECT *,
ROW_NUMBER () OVER (PARTITION BY CreateDateUTC ORDER BY PaymentType) AS N1
FROM AuditTable
WHERE CreateDateUTC <= '2017-11-02' AND CreateDateUTC >= '2017-10-01'
) ,
MAXP AS(
SELECT VendorID, PaymentType, CreateDateUTC
FROM CTE
WHERE N1 = (SELECT MAX(N1) FROM CTE)
)
SELECT TOP 1 MAXP.VendorID, MAXP.PaymentType AS PaymentType, CTE.PaymentType AS Prev_PaymentType
FROM MAXP
JOIN CTE ON CTE.VendorID = MAXP.VendorID;
Result:
+----------+-------------+------------------+
| VendorID | PaymentType | Prev_PaymentType |
+----------+-------------+------------------+
| 8048 | 7 | 2 |
+----------+-------------+------------------+
Demo
Here is a variant without using LEAD() or LAG() but does use ROW_NUMBER and COUNT() OVER().
See this verision work at:SQL Fiddle
CREATE TABLE AuditTable (
AuditID int
, VendorID int
, PaymentType int
, CreateDateUTC date
);
INSERT INTO AuditTable
VALUES (999, 8048, 2, '2017-10-30'),
(1000, 1234, 5, '2017-10-31'),
(1001, 8048, 7, '2017-10-31'),
(1002, 1234, 5, '2017-10-31'),
(1003, 1234, 7, '2017-10-31'),
(1004, 1234, 5, '2017-11-01');
Query 1:
WITH
rowz AS (
SELECT *
, ROW_NUMBER() OVER (PARTITION BY VendorID
ORDER BY CreateDateUTC, AuditID) AS lagno
FROM AuditTable
),
cte AS (
SELECT *
, ROW_NUMBER() OVER (PARTITION BY VendorID, CreateDateUTC
ORDER BY c DESC, span_dt) rn
FROM (
SELECT r1.AuditID, r1.VendorID, r1.CreateDateUTC
, r1.PaymentType AS prevpaymenttype
, r2.PaymentType
, COALESCE(r2.CreateDateUTC, CAST(GETDATE() AS date)) span_dt
, COUNT(*) OVER (PARTITION BY r1.VendorID, r1.CreateDateUTC, r1.PaymentType) c
FROM rowz r1
LEFT JOIN rowz r2 ON r1.VendorID = r2.VendorID
AND r1.lagno = r2.lagno - 1
) d
)
SELECT
AuditID, VendorID, PrevPaymentType, PaymentType, CreateDateUTC
FROM (
SELECT
*
FROM cte
WHERE ('20171031' BETWEEN CreateDateUTC AND span_dt AND rn = 1)
OR (CAST(GETDATE() AS date) BETWEEN CreateDateUTC AND span_dt AND rn = 1)
) d
WHERE PaymentType <> PrevPaymentType
Results:
| AuditID | VendorID | PrevPaymentType | PaymentType | CreateDateUTC |
|---------|----------|-----------------|-------------|---------------|
| 999 | 8048 | 2 | 7 | 2017-10-30 |

The highest value from list-distinct

Can anyone help me with query, I have table
vendorid, agreementid, sales
12001 1004 700
5291 1004 20576
7596 1004 1908
45 103 345
41 103 9087
what is the goal ?
when agreemtneid >1 then show me data when sales is the highest
vendorid agreementid sales
5291 1004 20576
41 103 9087
Any ideas ?
Thx
Well you could try using a CTE and ROW_NUMBER something like
;WITH Vals AS (
SELECT *, ROW_NUMBER() OVER(PARTITION BY AgreementID ORDER BY Sales DESC) RowID
FROM MyTable
WHERE AgreementID > 1
)
SELECT *
FROM Vals
WHERE RowID = 1
This will avoid you returning multiple records with the same sale.
If that was OK you could try something like
SELECT *
FROM MyTable mt INNER JOIN
(
SELECT AgreementID, MAX(Sales) MaxSales
FROM MyTable
WHERE AgreementID > 1
) MaxVals ON mt.AgreementID = MaxVals.AgreementID AND mt.Sales = MaxVals.MaxSales
SELECT TOP 1 WITH TIES *
FROM MyTable
ORDER BY DENSE_RANK() OVER(PARTITION BY agreementid ORDER BY SIGN (SIGN (agreementid - 2) + 1) * sales DESC)
Explanation
We break table MyTable into partitions by agreementid.
For each partition we construct a ranking or its rows.
If agreementid is greater than 1 ranking will be equal to ORDER BY sales DESC.
Otherwise ranking for every single row in partition will be the same: ORDER BY 0 DESC.
See how it looks like:
SELECT *
, SIGN (SIGN (agreementid - 2) + 1) * sales AS x
, DENSE_RANK() OVER(PARTITION BY agreementid ORDER BY SIGN (SIGN (agreementid - 2) + 1) * sales DESC) AS rnk
FROM MyTable
+----------+-------------+-------+-------+-----+
| vendorid | agreementid | sales | x | rnk |
+----------|-------------|-------+-------+-----+
| 0 | 0 | 3 | 0 | 1 |
| -1 | 0 | 7 | 0 | 1 |
| 0 | 1 | 3 | 0 | 1 |
| -1 | 1 | 7 | 0 | 1 |
| 41 | 103 | 9087 | 9087 | 1 |
| 45 | 103 | 345 | 345 | 2 |
| 5291 | 1004 | 20576 | 20576 | 1 |
| 7596 | 1004 | 1908 | 1908 | 2 |
| 12001 | 1004 | 700 | 700 | 3 |
+----------+-------------+-------+-------+-----+
Then using TOP 1 WITH TIES construction we leave only rows where rnk equals 1.
you can try like this.
SELECT TOP 1 sales FROM MyTable WHERE agreemtneid > 1 ORDER BY sales DESC
I really do not know the business logic behind agreement_id > 1. It looks to me you want the max sales (with ties) by agreement id regardless of vendor_id.
First, lets create a simple sample database.
-- Sample table
create table #sales
(
vendor_id int,
agreement_id int,
sales_amt money
);
-- Sample data
insert into #sales values
(12001, 1004, 700),
(5291, 1004, 20576),
(7596, 1004, 1908),
(45, 103, 345),
(41, 103, 9087);
Second, let's solve this problem using a common table expression to get a result set that has each row paired with the max sales by agreement id.
The select statement just applies the business logic to filter the data to get your answer.
-- CTE = max sales for each agreement id
;
with cte_sales as
(
select
vendor_id,
agreement_id,
sales_amt,
max(sales_amt) OVER(PARTITION BY agreement_id) AS max_sales
from
#sales
)
-- Filter by your business logic
select * from cte_sales where sales_amt = max_sales and agreement_id > 1;
The screen shot below shows the exact result you wanted.

Resources