Return back data horizontally - sql

Am kinda lost in here.
I have a table with multiple duplicate values in the same column which i want to return back with S/N and Group as one unique value and then the price of each package in separate column as ('1D' AS DAILY, '1W' AS WEEKLY, AND '1M' AS 'MONTHLY').
am using MS SQL 2012
Thanks in Advance
regards,
Adnan

select [S/N], [group],
sum(case when package = '1D' then price else 0 end) as [daily price],
sum(case when package = '1W' then price else 0 end) as [weekly price],
sum(case when package = '1M' then price else 0 end) as [monthly price]
from rates
group by [S/N], [group]

You could also use the PIVOT operator:
if OBJECT_ID('tempdb..#Rates') is not NULL drop table #Rates
create table #Rates(
S_N int not null,
_group varchar(10) not null,
Package char(2) not null,
price int not null
)
insert into #Rates
values (8, 'D' , '1M', 3700)
,(8, 'I-DC','1M', 1750)
,(8, 'TB', '1M', 7000)
,(12, 'A', '1D', 80)
,(12, 'A', '1W', 480)
,(12, 'A', '1M', 1800)
,(12, 'B', '1D', 90)
,(12, 'B', '1W', 540)
,(12, 'B', '1M', 2100)
select S_N, _group, pvt.[1D] as "DailyPrice1D", pvt.[1W] as "WeeklyPrice1W", pvt.[1M] as "MonthlyPrice1M"
from #Rates
pivot(avg(price) FOR Package in ("1D", "1W", "1M")) pvt
ORDER by S_N
Do you need more explanation or is this code sample enough?
For more information here is the Microsoft documentation: https://learn.microsoft.com/en-us/sql/t-sql/queries/from-using-pivot-and-unpivot?view=sql-server-2017

Related

How to sum and subtract one column value based on percentage in SQL Server 2008

DECLARE #BalanceTblRec TABLE
(
NetAmount decimal(18, 3),
Percentage int,
[Description] nvarchar(max)
)
DECLARE #BalanceTblPay TABLE
(
NetAmount decimal(18, 3),
Percentage int,
[Description] nvarchar(max)
)
INSERT INTO #BalanceTblRec
VALUES (21, 11, 'ReceiveReceipt'),
(20, 11, 'ReceiveReceipt'),
(20, 10, 'ReceiveReceipt'),
(20, 20, 'ReceiveReceipt'),
(10, 10, 'ReceiveReceipt')
INSERT INTO #BalanceTblPay
VALUES (10, 11, 'PayReceipt'),
(10, 11, 'PayReceipt'),
(10, 2, 'PayReceipt'),
(5, 15, 'PayReceipt'),
(30, 10, 'PayReceipt'),
(20, 10, 'PayReceipt')
;WITH MaPercentage AS
(
SELECT
Percentage,
SUM(NetAmount) AS Net,
'Receive' AS Flag
FROM
#BalanceTblRec
GROUP BY
Percentage
UNION ALL
SELECT
Percentage,
SUM(NetAmount) AS Net,
'Pay' AS Flag
FROM
#BalanceTblPay
GROUP BY
Percentage
)
SELECT * FROM MaPercentage
Now here I want subtract net from net based on falg, receive - pay based on percentage.
Like this:
Per Net Flag
-----------------------
10 30.000 - 50 Receive
11 41.000 - 20 Receive
20 20.000 Receive
2 10.000 Pay
15 5.000 Pay
I think this is what you want:
DECLARE #BalanceTblRec TABLE (NetAmount decimal(18,3), Percentage int, [Description] nvarchar(max))
DECLARE #BalanceTblPay TABLE (NetAmount decimal(18,3), Percentage int, [Description] nvarchar(max))
insert into #BalanceTblRec values (21, 11, 'ReceiveReceipt'),(20, 11, 'ReceiveReceipt'),(20, 10, 'ReceiveReceipt'),(20, 20, 'ReceiveReceipt'), (10, 10, 'ReceiveReceipt')
insert into #BalanceTblPay values (10, 11, 'PayReceipt'),(10, 11, 'PayReceipt'),(10, 2, 'PayReceipt'),(5, 15, 'PayReceipt'),(30, 10, 'PayReceipt') ,(20, 10, 'PayReceipt')
;WITH MaPercentage as (
select Percentage, sum(NetAmount) as Net, 'Receive' as Flag from #BalanceTblRec group by Percentage
union all
select Percentage, -sum(NetAmount) as Net, 'Pay' as Flag from #BalanceTblPay group by Percentage
)
select
Percentage,
abs(sum(net)) as SumNet,
case when sum(net) > 0 then 'Receive'
else 'Pay'
end as Flag
from MaPercentage
group by Percentage
Just changed the sign in the Pays and sum groupping by percentage.
Another way is to FULL JOIN the receivements with the payments.
;WITH RCV AS (
select Percentage, sum(NetAmount) as Net
from #BalanceTblRec
group by Percentage
)
, PAY AS (
select Percentage, sum(NetAmount) as Net
from #BalanceTblPay
group by Percentage
)
SELECT
COALESCE(r.Percentage, p.Percentage) AS Percentage,
ABS(COALESCE(r.Net, 0) - COALESCE(p.Net, 0)) AS Net,
(CASE
WHEN (COALESCE(r.Net, 0) - COALESCE(p.Net, 0)) < 0 THEN 'Pay'
ELSE 'Receive'
END) AS Flag
FROM RCV r
FULL JOIN PAY p ON p.Percentage = r.Percentage

SQL Dynamically Joining Tables on Various Columns

First time posting!
Have a use case where we want to join some sales data to a master agreement table to determine applicable fee's at a transactional level.
The hard part is that the agreement table has VARIOUS possibilities, and in a worse case scenario at least a "catch all".
We would want to start at the *most granular" level. So the purple line matches on all possible values.
However, a field like the blue sales record does not match on any value to the master except supplier, so in that case it is a catch all.
I've thought of concat'ing all the rows in the master, but then I'd need to find a way of joining it to sales? a simple concat would not successfully join the blue row example together. So it's like the join would have to dynamically choose which columns to compare.
By chance would any users have some idea's on how to achieve this?
Thanks!
(Code for tables)
create TABLE T_TEST_AGREEMENT (
SUPPLIER VARCHAR(254),
ITEM VARCHAR(254),
PROGRAM INT,
RXDA VARCHAR(254),
CTRCT INT,
FEE INT
);
create TABLE T_TEST_AGREEMENT_SALES (
SUPPLIER VARCHAR(254),
ITEM VARCHAR(254),
PROGRAM INT,
RXDA VARCHAR(254),
CTRCT INT
);
INSERT INTO T_TEST_AGREEMENT values
(123, 'A', 60, 'Y', 4, 1),
(123, 'A', 61, 'N', 4, 2),
(123, 'B', 62, null, 5, 3),
(123, 'C', null, 'Y', 6, 4),
(123, null, 63, null, null, 5),
(123, null, null, 'Y', null, 6),
(123, null, null, null, null, 7);
INSERT INTO T_TEST_AGREEMENT_SALES values
(123, 'D', 63, null, null),
(123, 'F', null, null, null),
(123, 'A', 61, 'N', 4),
(123, 'C', null, 'Y', 6);
You can use a correlated subquery:
select st.*,
(select m.fee
from master m
where m.supplier = st.supplier and
(m.item is null or m.item = st.item) and
(m.program is null or m.program = st.program) and
(m.rxda is null or m.rxda = st.rxda) and
(m.ctrct is null or m.ctrct = st.ctrct)
order by ( (case when m.item = st.item then 1 else 0 end) +
(case when m.program = st.program then 1 else 0 end) +
(case when m.rxda = st.rxda then 1 else 0 end) +
(case when m.ctrct = st.ctrct then 1 else 0 end) +
) desc
fetch first 1 row only
) as fee
from sales_transactions st;
This uses standard SQL syntax. It might vary depending on your row.

SQL Server: how to form a crosstab

I am using SQL Server 2014.
How can I create a cross-table out of the straight table like the below?
Code here where I try to do the multiple-pivoting.
Sample input
Code here only about the sample data.
GOAL after pivoting and reformatting of the columns by other columns
This logic would work for you:
Create an additional temp table where you store all the possible combinations of Store and City, then left join with your existing table.
From this left join you get the "calculated" string that you will use as a column name (C1_L_xCount, C1_L2_xCount, etc).
Then apply a pivot as below:
select ylabel.colx, ylabel.coly, y.myid, y.week, isnull(y.xCount, 0) xCount, isnull(y.yCount, 0) yCount
into #table
from (
select distinct y3.week, y1.city, y2.store, y1.city + '_' + y2.store + '_xCount' as colx, y1.city + '_' + y2.store + '_yCount' as coly
from #yt y1 cross join #yt y2 cross join #yt y3
) ylabel left join #yt y on y.week = ylabel.week and y.store = ylabel.store and y.city = ylabel.city
select * from #table --this is the additional table, the one used for pivoting
--this is your solution:
select myid
, week
, isnull(C1_L_xCount, 9) C1_L_xCount
, isnull(C1_L2_xCount, 9 ) C1_L2_xCount
, isnull(C2_L_xCount, 0) C2_L_xCount
, isnull(C2_L2_xCount, 0) C2_L2_xCount
, isnull(C1_L_yCount, 0) C1_L_yCount
, isnull(C1_L2_yCount, 0) C1_L2_yCount
, isnull(C2_L_yCount, 0) C2_L_yCount
, isnull(C2_L2_yCount, 0) C2_L2_yCount
from
(
select *
from #table
pivot ( max(xCount) for colx in ( [C1_L_xCount], [C1_L2_xCount],[C2_L_xCount], [C2_L2_xCount])) p
pivot ( max(yCount) for coly in ( [C1_L_yCount], [C1_L2_yCount],[C2_L_yCount], [C2_L2_yCount])) q
where myid is not null
) t
Please check a working demo here.
But, if you need to dynamically add Stores and Cities, you will need to convert this into a dynamic pivot.
We repeat the case such that
CASE WHEN
[City]='C1' AND [Store]='L'
THEN
[xCount]
END
AS 'LC1_xCount',
I changed the valuations a little bit to get more combinations, code here.
or code here:
CREATE TABLE #yt
([MyID] int, [Store] nvarchar(300), [City] nvarchar(300), [Week] int, [xCount] int, [yCount] int)
;
INSERT INTO #yt
([MyID], [Store], [City], [Week], [xCount], [yCount])
VALUES
(1, 'L', 'C1', 1, 96, 7),
(2, 'L', 'C1', 1, 138, 77),
(3, 'L2', 'C1', 1, 37, 744),
(4, 'L', 'C1', 1, 59, 74),
(5, 'L', 'C1', 2, 282,73333),
(6, 'L2', 'C2', 2, 212,7333),
(7, 'L2', 'C2', 2, 78,733),
(8, 'L', 'C2', 2, 97,73),
(9, 'L', 'C2', 3, 60,72222),
(10, 'L2', 'C2', 3, 123,7222),
(11, 'L2', 'C1', 3, 220,722),
(12, 'L2', 'C1', 3, 87,72)
;
select [MyId], [Week], [LC1_xCount], [LC2_xCount], [L2C1_xCount], [L2C2_xCount]
, [LC1_yCount], [LC2_yCount], [L2C1_yCount], [L2C2_yCount]
from
(
select myid, week, store, city, xcount, ycount,
CASE WHEN
[City]='C1' AND [Store]='L'
THEN
[xCount]
END
AS 'LC1_xCount',
CASE WHEN
[City]='C2' AND [Store]='L'
THEN
[xCount]
END
AS 'LC2_xCount',
CASE WHEN
[City]='C1' AND [Store]='L2'
THEN
[xCount]
END
AS 'L2C1_xCount',
CASE WHEN
[City]='C2' AND [Store]='L2'
THEN
[xCount]
END
AS 'L2C2_xCount',
CASE WHEN
[City]='C1' AND [Store]='L'
THEN
[yCount]
END
AS 'LC1_yCount',
CASE WHEN
[City]='C2' AND [Store]='L'
THEN
[yCount]
END
AS 'LC2_yCount',
CASE WHEN
[City]='C1' AND [Store]='L2'
THEN
[yCount]
END
AS 'L2C1_yCount',
CASE WHEN
[City]='C2' AND [Store]='L2'
THEN
[yCount]
END
AS 'L2C2_yCount'
from #yt
GROUP BY myid, week, store,city, xcount, ycount
) src;

Selecting last date available and one before last

I have a table #sales in which I record sales and profits
The select below gives me the summary of sales and profits for 2 specific dates
The challenge is as follows:
Instead of hard-coding the date, I would need to replace it with:
First date: get the last available date. I though of using max(fecha_valor) but I get an error.
Second date: the one before last available date
so in the example below would be:
First date: '20140714'
Second date: '20140712'
Is it possible to have an additional column with columna_1_p / columna_1 ?
Can anyone help with this?
DECLARE #sales TABLE
(
custom VARCHAR(10) NOT NULL,
fecha_valor DATE NOT NULL,
sales NUMERIC(10, 2) NOT NULL,
profits NUMERIC(10, 2) NOT NULL
);
INSERT INTO #sales(Custom, Fecha_valor, sales, profits)
VALUES ('q', '20140708', 51,21),
('q', '20140712', 3,33),
('q', '20140712', 5,12),
('q', '20140711', 6,43),
('q', '20140712', 2,66),
('q', '20140712', 7,21),
('q', '20140714', 24,76),
('q', '20140714', 24,12),
('x', '20140709', 25,0),
('x', '20140710', 16,0),
('x', '20140711', 66,31),
('x', '20140712', 23,12),
('x', '20140712', 35,11),
('x', '20140714', 57,1),
('c', '20140712', 97,2),
('c', '20140714', 71,3);
SELECT
custom,
CAST(SUM(Case fecha_valor when '2014-07-12' then sales ELSE 0 END) AS numeric(12, 3)) as columna_1,
CAST(SUM(Case fecha_valor when '2014-07-14' then sales ELSE 0 END) AS numeric(12, 3)) as columna_2,
CAST(SUM(Case fecha_valor when '2014-07-12' then profits ELSE 0 END) AS numeric(12, 3)) as columna_1_P,
CAST(SUM(Case fecha_valor when '2014-07-14' then profits ELSE 0 END) AS numeric(12, 3)) as columna_2_P
FROM
#sales
GROUP BY
custom;
You can use following query:
DECLARE #FirstDate DATE,
#SecondDate DATE
SELECT #FirstDate = MAX(fecha_valor) FROM #Sales
SELECT #SecondDate = MAX(fecha_valor) FROM #Sales WHERE fecha_valor<> #FirstDate
SELECT custom,
CAST(SUM(Case fecha_valor when #SecondDate THEN sales ELSE 0 END) AS numeric(12, 3)) as columna_1,
CAST(SUM(Case fecha_valor when #FirstDate THEN sales ELSE 0 END) AS numeric(12, 3)) as columna_2,
CAST(SUM(Case fecha_valor when #SecondDate THEN profits ELSE 0 END) AS numeric(12, 3)) as columna_1_P,
CAST(SUM(Case fecha_valor when #FirstDate THEN profits ELSE 0 END) AS numeric(12, 3)) as columna_2_P
FROM #sales
GROUP BY custom;
Not sure on what exactly you are looking for in your final result but would something along the lines of the below work?
SELECT Custom, Fecha_valor, sales, profits FROM sales ORDER BY Fecha_valor DESC LIMIT 2

30/60/90 Day Sumation Query

I have the following query. It pulls invoice and time entries, and calculates the EHR (effective hourly rate) for each client, on a per month basis. What I need to get is:
company ,agreement ,lastMonthEHR,60dayEHR,90dayEHR,6MoEHR,12MoEHR,LifeEHR
CompanyA,AgreementB, 30.45, 27.76, 55.22, 30.75, 30.00, 25.00
EDIT:
I apologize for the format. I'll see if I can format it better. The following query returns monthly invoices, with EHR calculated.
SELECT a.AGR_Name, AGR_Type.AGR_Type_Desc, c.Company_Name, ap.InvoiceDate,ap.Revenue,ap.Hours,ap.EHR
FROM AGR_Header AS a INNER JOIN Company AS c ON a.Company_RecID = c.Company_RecID
LEFT JOIN AGR_Type ON a.AGR_Type_RecID = AGR_Type.AGR_Type_RecID
LEFT JOIN (
SELECT ar.AGR_Header_RecID,ar.Revenue,ac.InvoiceDate,ISNULL (ac.Hours, 0) AS Hours,
CASE
WHEN ac.Hours IS NULL THEN (ar.Revenue)
WHEN ac.Hours <= 1 THEN (ar.Revenue)
ELSE CAST (ar.Revenue / NULLIF (ac.Hours,0) as NUMERIC (9,2))
END AS 'EHR'
FROM (
SELECT ah.AGR_Header_RecID,
DATEADD(month,ai.Month_Nbr-1,dateadd(year,ai.Year_Nbr-2000,'2000-01-01')) as InvoiceDate,
CAST (ai.Monthly_Inv_Amt AS NUMERIC (9, 2)) AS Revenue
FROM
dbo.AGR_Header AS ah INNER JOIN
dbo.AGR_Invoice_Amt AS ai ON ah.AGR_Header_RecID = ai.AGR_Header_RecID
GROUP BY ah.AGR_Header_RecID, ai.Month_Nbr, ai.Year_Nbr) as ar
LEFT JOIN (
SELECT ah.AGR_Header_RecID,SUM(te.Hours_Actual) AS Hours, dateadd(month, datediff(month,0,te.Date_start),0) as InvoiceDate
FROM
dbo.Time_Entry AS te INNER JOIN
dbo.AGR_Header AS ah ON te.Agr_Header_RecID = ah.AGR_Header_RecID
WHERE (te.Agr_Header_RecID IS NOT NULL) AND (te.Agr_Hours IS NOT NULL)
GROUP BY ah.AGR_Header_RecID, dateadd(month, datediff(month,0,te.Date_Start),0)) AS ac ON ar.AGR_Header_RecID = ac.AGR_Header_RecID
AND ar.InvoiceDate = ac.InvoiceDate) AS ap ON ap.AGR_Header_RecID = a.AGR_Header_RecID
ORDER BY Company, Agreement, InvoiceDate
The SQL you posted is pretty complicated, but I think it can be simplified. I think the key is to get the invoice data into a format that is similar to the following:
DECLARE #invoice AS TABLE(
[ID] INT,
[CompanyID] INT,
[InvoiceDate] DATE,
[Hours] DECIMAL(9,2),
[Revenue] DECIMAL(9,2))
From there, the calculations are pretty simple, and they can be done using CASE WHEN statements with minimal subselects (I used one just for clarity, but even that one could be eliminated). Here's a full working example for SQL Server:
--Setup table and dummy data
DECLARE #invoice AS TABLE(
[ID] INT,
[CompanyID] INT,
[InvoiceDate] DATE,
[Hours] DECIMAL(9,2),
[Revenue] DECIMAL(9,2))
INSERT INTO #invoice VALUES(1, 1, '2013-01-01', 5, 100)
INSERT INTO #invoice VALUES(2, 1, '2013-02-01', 6, 100)
INSERT INTO #invoice VALUES(3, 1, '2013-03-01', 7, 100)
INSERT INTO #invoice VALUES(4, 1, '2013-04-01', 8, 100)
INSERT INTO #invoice VALUES(5, 1, '2013-05-01', 9, 100)
INSERT INTO #invoice VALUES(6, 1, '2013-06-01', 10, 100)
INSERT INTO #invoice VALUES(7, 1, '2013-07-01', 11, 100)
INSERT INTO #invoice VALUES(8, 1, '2013-08-01', 12, 100)
INSERT INTO #invoice VALUES(9, 2, '2013-04-01', 5, 100)
INSERT INTO #invoice VALUES(10, 2, '2013-05-01', 6, 100)
INSERT INTO #invoice VALUES(11, 2, '2013-06-01', 7, 100)
INSERT INTO #invoice VALUES(12, 2, '2013-07-01', 8, 100)
--Calculate last month start and end dates
--Hardcoded here for brevity
DECLARE #lastMonthStartDate AS DATETIME
DECLARE #lastMonthEndDate AS DATETIME
SET #lastMonthStartDate = '2013-08-01'
SET #lastMonthEndDate = '2013-09-01'
--Calculate EHRs for different time periods
SELECT
A.CompanyID,
CASE WHEN A.LastMonthHours = 0 THEN 0 ELSE A.LastMonthRevenue / A.LastMonthHours END as [LastMonthEHR],
CASE WHEN A.Last60DaysHours = 0 THEN 0 ELSE A.Last60DaysRevenue / A.Last60DaysHours END as [Last60DaysEHR],
CASE WHEN A.Last90DaysHours = 0 THEN 0 ELSE A.Last90DaysRevenue / A.Last90DaysHours END as [Last90DaysEHR]
FROM (
SELECT
[CompanyID],
SUM(CASE WHEN [InvoiceDate] >= #lastMonthStartDate AND [InvoiceDate] < #lastMonthEndDate THEN [Hours] ELSE 0 END) as [LastMonthHours],
SUM(CASE WHEN [InvoiceDate] >= #lastMonthStartDate AND [InvoiceDate] < #lastMonthEndDate THEN [Revenue] ELSE 0 END) as [LastMonthRevenue],
SUM(CASE WHEN [InvoiceDate] >= DATEADD(d, -60, GETDATE()) THEN [Hours] ELSE 0 END) as [Last60DaysHours],
SUM(CASE WHEN [InvoiceDate] >= DATEADD(d, -60, GETDATE()) THEN [Revenue] ELSE 0 END) as [Last60DaysRevenue],
SUM(CASE WHEN [InvoiceDate] >= DATEADD(d, -90, GETDATE()) THEN [Hours] ELSE 0 END) as [Last90DaysHours],
SUM(CASE WHEN [InvoiceDate] >= DATEADD(d, -90, GETDATE()) THEN [Revenue] ELSE 0 END) as [Last90DaysRevenue]
FROM #invoice
GROUP BY [CompanyID]
) A
I think this approach should work for you. I know I had to simply the problem to illustrate the way I would approach a query like this in my answer, so if you need me to expand on it please let me know.