How to get the column sums in row? - sql

I have a table Expense where monthly expense is stored.
Now I want to get a result like "output". Here ID will be set according to the month sequence hence December will get 12.
How can I achieve that? I tried Unpivot but cant achieve it.

You can use apply :
select tt.id, sum(tt.monval) as TotalExpense
from Expense t cross apply
( values (1, January), (2, February), (3, March) ) tt(id, monval)
group by tt.id;

Related

SQL script to with the shown screenshot

I want to write a sql script to as shown in the screenshot image. Thank you.
enter image description here
I've tried MAX() function to aggregate the ESSBASE_MONTH field to make it distinct and display a single month in the output instead of multiple months. I am yet to figure out how to put 0 in any month that EMPID did not perform any sale like in December under "Total GreaterThan 24 HE Account" and "Total_HE_Accounts"
The fields of the table are not very informative however based on screenshot, this is the best answer I could come up with.
Assuming the table name is SALES;
select
ADJ_EMPID,
ESSBASE_MONTH,
MAX(YTD_COUNT) AS YTD_COUNT,
SUM(TOTAL_24) AS TOTAL_24,
SUM(TOTAL_ACC) AS TOTAL_ACC
from SALES
group by
ADJ_EMPID,
ESSBASE_MONTH
The above will aggregate the monthly 'sales' data as expected.
To add the 'missing' rows such as the December, it is possible to do it by doing a union of the above query with a vitural table.
select
MAX(MONTH_NUMBER) AS MONTH_NUMBER,
ADJ_EMPID,
ESSBASE_MONTH,
MAX(YTD_COUNT) AS YTD_COUNT,
SUM(TOTAL_24) AS TOTAL_24,
SUM(TOTAL_ACC) AS TOTAL_ACC
from (
select
1 as MONTH_NUMBER,
*
from SALES
union all
select * from (values
(1, '300014366', 'January', 0, 0, 0),
(2, '300014366', 'Feburary', 0, 0, 0),
-- add the other missing months as required
(11, '300014366', 'November', 0, 0, 0),
(12, '300014366', 'December', 0, 0, 0)
) TEMP_TABLE (MONTH_NUMBER, ADJ_EMPID, ESSBASE_MONTH, YTD_COUNT, TOTAL_24, TOTAL_ACC)
) as AGGREGATED_DATA
group by
ADJ_EMPID,
ESSBASE_MONTH
order by MONTH_NUMBER;
TEMP_TABLE is a vitural tables which contains all the months and sales as zero. There is a special field MONTH_NUMBER added to sort the months in the proper order.
Not the easiest query to understand, the requirement is not exactly feasible either..
Link to fiddledb for a working solution with PostgreSQL 15.

Calculating the percentage of different types of customer feedback in each quarter

The problem statement is: I have a table (order_t) which has customer feedback (one column) and quarter number (as another column).
Using a CTE, I need to calculate the percentage of number of customer feedback in each category as well as the total number of customer feedback in each quarter.
After this happens, I need the percentage of different types of customer feedback (like good, bad, ok, very good, very bad) but using CTE.
How can I solve this statement?
I try to solve customer feedback as
WITH total_feedback AS
(
SELECT *
COUNT(CUSTOMER_FEEDBACK), QUARTER NUMBER
FROM
table1
GROUP BY
2
)
But I'm unable to calculate the first half portion, i.e. percentage of different types of customer feedback in each quarter using CTE.
How can I do that?
Find the file of the data
What you could do, and I'll keep the example as close to the code you provided as possible, is the following - using 2 CTE's:
WITH total_feedback AS (
SELECT COUNT(CUSTOMER_FEEDBACK) AS total_feedback, QUARTER_NUMBER
FROM table1
GROUP BY 2
),
category_feedback AS (
SELECT COUNT(CUSTOMER_FEEDBACK) AS feedback_count, CUSTOMER_FEEDBACK, QUARTER_NUMBER
FROM table1
GROUP BY 2, 3
)
SELECT
category_feedback.CUSTOMER_FEEDBACK,
category_feedback.QUARTER_NUMBER,
(feedback_count / total_feedback.total_feedback) * 100 AS feedback_percentage
FROM category_feedback
INNER JOIN total_feedback
ON category_feedback.QUARTER_NUMBER = total_feedback.QUARTER_NUMBER

Is there a way to limit a sum up to a calculated date in a table?

I have a table with SentDate and RefundAmounts. I would like to sum up the amounts on each row from the date until a year into the future for every line.
In the example below I would like to add a column that says sum for the year.
This sum should be for the first line the sum of refunds from '2006-12-14' until '2007-12-14' which would be 3696,22 as there were no refunds during that period.
The second row would be from '2007-12-24' until '2008-12-24' which would be 463,05
SentDate YearAhead RefundAmount
2006-12-14 2007-12-14 3696,22
2007-12-24 2008-12-24 394,35
2008-12-18 2009-12-18 44,33
2008-12-19 2009-12-19 24,37
2009-12-16 2010-12-16 21,88
I have tried something along the lines of
select SentDate, dateadd(year,1,sentdate) YearAhead, SumRefund
from table
but I have no idea how to get the annual future sum for each row
Thanks for the suggestion. The final result should look as follows:
SentDate YearAhead RefundAmount SumForYear
2006/12/14 2007/12/14 3696,22 3696,22
2007/12/24 2008/12/24 394,35 463,05
2008/12/18 2009/12/18 44,33 90,58
2008/12/19 2009/12/19 24,37 46,25
2009/12/16 2010/12/16 21,88 21,88
You need to join with a BETWEEN to gather all rows that compose that period, then use GROUP BY with SUM to the result.
select
T.SentDate,
dateadd(year, 1, T.SentDate) YearAhead,
SUM(P.SumRefund) AS TotalOverYear
from
YourTable AS T
INNER JOIN YourTable AS P ON P.SentDate BETWEEN T.SendDate AND DATEADD(YEAR, 1, T.SendDate)
GROUP BY
T.SentDate
Something like this should suite your needs if you always need a complete year:
select sum(refund) as refundAmounts,year(min(SentDate)) as year from yourTable group by year(SentDate)
the year function gets only the year part from a datetime row and with group by you can split the aggregation of sum and min function to different groups.

Calculate clients per week based on start date and end date in MS Access

In a MS Access DB I have information about clients' arrival and departure dates. Based on this I would like to calculate the number of clients that will be visiting per week.
Consider the example below. Peter arrives in the first week of January and leaves on the third week (weeks start on Sunday). Mary on the other hand arrives and leaves in the first week.
I would like the output to be as shown below, with the week number in the first column and the total number of guests in the second.
What is the best way of achieving this please?
My solution with german date format, test with ORACLE. I build some calendar table (tw_test_week) for every week to join it.
CREATE TABLE tw_test_client (
client VARCHAR2(10),
arrival DATE,
departure DATE
);
INSERT INTO tw_test_client VALUES
( 'Peter', to_date('01.01.2018','DD.MM.YYYY'), to_date('11.01.2018','DD.MM.YYYY'));
INSERT INTO tw_test_client VALUES
( 'Mary', to_date('01.01.2018','DD.MM.YYYY'), to_date('01.02.2018','DD.MM.YYYY'));
CREATE TABLE tw_test_week (
weekid INT,
started DATE,
ended DATE
);
INSERT INTO tw_test_week VALUES
( to_char(to_date('01.01.2018','DD.MM.YYYY'),'WW'),
to_date('01.01.2018','DD.MM.YYYY'),
to_date('07.01.2018','DD.MM.YYYY')
);
INSERT INTO tw_test_week VALUES
( to_char(to_date('08.01.2018','DD.MM.YYYY'),'WW'),
to_date('08.01.2018','DD.MM.YYYY'),
to_date('14.01.2018','DD.MM.YYYY')
);
INSERT INTO tw_test_week VALUES
( to_char(to_date('15.01.2018','DD.MM.YYYY'),'WW'),
to_date('15.01.2018','DD.MM.YYYY'),
to_date('21.01.2018','DD.MM.YYYY')
);
INSERT INTO tw_test_week VALUES
( to_char(to_date('22.01.2018','DD.MM.YYYY'),'WW'),
to_date('22.01.2018','DD.MM.YYYY'),
to_date('28.01.2018','DD.MM.YYYY')
);
INSERT INTO tw_test_week VALUES
( to_char(to_date('29.01.2018','DD.MM.YYYY'),'WW'),
to_date('29.01.2018','DD.MM.YYYY'),
to_date('04.02.2018','DD.MM.YYYY')
);
SELECT w.weekid, COUNT(*)
FROM tw_test_week w
JOIN tw_test_client c
ON w.started BETWEEN c.arrival and c.departure
GROUP BY w.weekid
ORDER BY w.weekid;
Result
WEEKID COUNT
1 2
2 2
3 1
4 1
5 1
Create a table containing the numbers 1 to 53. This table in my query is called WeekNumTable and contains a single field with each week number listed.
SELECT WeekNum
, COUNT(WeekNum) AS TotalClients
FROM WeekNumTable INNER JOIN ClientTable ON
WeekNumTable.WeekNum>=DatePart("ww",ClientTable.Arrival-Weekday(ClientTable.Arrival,1)+7) AND
WeekNumTable.WeekNum<=DatePart("ww",ClientTable.Departure-Weekday(ClientTable.Departure,1)+7)
GROUP BY WeekNum
Your example confused me a little as you list week 3 as having 1 client, while the data table doesn't.
Edit: The one problem that I can see with this is that if you do want to look at the next 100 weeks you'll need something to separate the years otherwise week 1 from both years will be lumped together.
You can use a series of queries to obtain this.
First, create a query named Ten:
SELECT DISTINCT Abs([id] Mod 10) AS N
FROM MSysObjects;
Then you can create a query, ClientDays, that lists all your dates between first arrival and latest departure:
SELECT DISTINCT
[Ten_0].[N]+[Ten_1].[N]*10+[Ten_2].[N]*100 AS Id,
DateAdd("d",[Ten_0].[N]+[Ten_1].[N]*10+[Ten_2].[N]*100,[StartDate]) AS [Date]
FROM
Ten AS Ten_0,
Ten AS Ten_1,
Ten AS Ten_2,
(Select
Min([Arrival]) As StartDate,
DateDiff("d", Min([Arrival]), Max([Departure])) As Days
From
ClientDates) AS T
WHERE
((([Ten_0].[N]+[Ten_1].[N]*10+[Ten_2].[N]*100)<=[Days])
AND
((Ten_0.N)<=[Days]\1)
AND
((Ten_1.N)<=[Days]\10)
AND
((Ten_2.N)<=[Days]\100));
Use this in yet a query, ClientWeeks, to find the week numbers:
SELECT
Year([Date]) AS [Year],
DatePart("ww",[Date]) AS Week,
ClientDates.Client
FROM
ClientDays,
ClientDates
WHERE
ClientDays.Date Between [Arrival] And [Departure]
GROUP BY
Year([Date]),
DatePart("ww",[Date]),
ClientDates.Client;
Finally, count the clients:
SELECT
ClientWeeks.Year,
ClientWeeks.Week,
Count(ClientWeeks.Client) AS TotalClients
FROM
ClientWeeks
GROUP BY
ClientWeeks.Year,
ClientWeeks.Week;
Please note, that you will have trouble counting around New Year as you intend to use an inconsistent week numbering method.
The only week number that is unambiguous is the ISO-8601 system of yyyy-ww because the first and/or last week will cross the calendar year boundaries.
Leave a note if you wish to implement this as it cannot be done with native VBA functions; custom functions must be used, but I don't wish to post them here if you will not use them.

How can I display actuals and forecasts in the same period in a report without it getting silly?

So I have a system where users can forecast up until the 18th day following the end of a month. However, during the month they are entering actuals on a day to day basis.
So say the current date is 12th September 2012. I have a forecast for this month of 100 units and I already have actuals entered into the system of 25 units.
Because the 18th October hasn't been reached I would report September as having a forecast figure of 100 units. However, the users want to also be able to see the 25 units that they have entered to date.
I have two measures in my report, one for Actuals and one for Forecasts. If I allow the actuals for September to appear in the report then I will be displaying a figure of 125 units which is obviously incorrect and will make my year total also incorrect.
I considered adding a new measure "Transient Actuals", that would only be used in the current month and wouldn't accumulate, but this seems crazy!
I don't think the technologies really matter at all but I am using a SQL Server database and the report is an Analysis Services cube.
Surely this must be a common problem?
Okay - I do have a schema but it is very, very complicated and there are multiple layers between the report and the database tables. However, I can give you a simple SQL example.
--Actuals and Forecast Tables
DECLARE #Actuals TABLE (
[Month] INT,
Value NUMERIC(19,2));
DECLARE #Forecast TABLE (
[Month] INT,
Value NUMERIC(19,2));
--Pretending we are in Month #3 put in some dummy data
INSERT INTO #Actuals VALUES (1, 100);
INSERT INTO #Actuals VALUES (2, 120);
INSERT INTO #Actuals VALUES (3, 10);
INSERT INTO #Forecast VALUES (1, 90);
INSERT INTO #Forecast VALUES (2, 90);
INSERT INTO #Forecast VALUES (3, 90);
--Calculate the latest actuals period (this would usually be time-based)
DECLARE #LatestActualsPeriod INT = 2;
--Now report the data to the user
SELECT
[Month],
'Actuals' AS Source,
Value
FROM
#Actuals
WHERE
[Month] <= #LatestActualsPeriod
UNION ALL
SELECT
[Month],
'Forecast',
Value
FROM
#Forecast
WHERE
[Month] > #LatestActualsPeriod;
Now the results of that will be a table with the following data:
Month Source Value
1 Actuals 100.00
2 Actuals 120.00
3 Forecast 90.00
So where do I put the 10.00 actuals in Month 3 without making the totals incorrect and still displaying the forecast for the same month somewhere?
Okay, thanks to Jeremy I think I have the answer... the problem is that I have data like this in my report:
Row Labels Actual Forecast Total
2012 23.840 18.840 42.680
2012/Jan 3.580 0.000 3.580
2012/Feb 3.520 0.000 3.520
2012/Mar 4.000 0.000 4.000
2012/Apr 3.350 0.000 3.350
2012/May 3.440 0.000 3.440
2012/Jun 3.090 0.000 3.090
2012/Jul 2.860 0.000 2.860
2012/Aug 0.000 4.990 4.990
2012/Sep 0.000 3.500 3.500
2012/Oct 0.000 4.130 4.130
2012/Nov 0.000 2.710 2.710
2012/Dec 0.000 3.510 3.510
Grand Total 23.840 18.840 42.680
This is an Analysis Services cube so I can't have anything like "90 Actual/ 10 Forecast" displaying, each cell must contain a number. However, I have another solution. If I add a new dimension with two members I can use this to control what is displayed.
So the new dimension will have two choices, "Show All" or "Show Relevant" (I might need to work on the names). If the user has the "Show All" member selected then they will see any Actuals and Forecasts that the system holds. So this means that their total will be meaningless as it will include forecasts AND actuals for months up to and including the present month and just forecasts for future months.
However, if the user selects the "Show Relevant" member then they will only see the Actuals for months that haven't been completed and forecasts for future months (as shown above).
This will mean that every report will need to include this dimension and force the users to pick one of the members or their figures will be totally meaningless so it isn't the most elegant solution.
I'm making a bunch of assumptions here, but hopefully this will be useful here. I took your query and am showing you some other ways to do it that I think could help in your problem of showing the data in an easier to read way, if I'm understanding the problem correctly.
In the first example I'm making the assumption that you always put the date as the first of the month. If not then it could get a little trickier, or you could use DATEPART(MONTH, ... to make it work similarly as if you were storing it as an INT. I'm not assuming that every month will have a forecasted value or an actual value.
In the second example, to make it easier, I assumed that you want to show only the actuals up to the latest month with actuals. I then assume that you want the forecasted value and actuals (if there are any) for that month and beyond. I'm also assuming that there is a forecasted value for each month in the second query.
If any of these assumptions are incorrect, then the query can be modified further to handle that.
--Actuals and Forecast Tables
DECLARE #Actuals TABLE (
[Month] DATE,
Value NUMERIC(19,2));
DECLARE #Forecast TABLE (
[Month] DATE,
Value NUMERIC(19,2));
INSERT INTO #Actuals VALUES ('20120801', 100);
INSERT INTO #Actuals VALUES ('20120901', 120);
INSERT INTO #Actuals VALUES ('20121001', 10);
INSERT INTO #Forecast VALUES ('20120801', 90);
INSERT INTO #Forecast VALUES ('20120901', 90);
INSERT INTO #Forecast VALUES ('20121001', 90);
INSERT INTO #Actuals VALUES ('20121101', 50);
INSERT INTO #Forecast VALUES ('20121201', 90);
-- If you can't have an actuals record without a forcast record, remove all the ISNULL'ing except the first ISNULL on the [Actual/Forecast] line
-- and change to LEFT JOIN instead of FULL.
SELECT
SUBSTRING(CONVERT(VARCHAR, ISNULL(F.[Month], A.[Month]), 103), 4, 999) [Report Month]
, ISNULL(CAST(A.Value AS VARCHAR(20)), '-') + '/' + ISNULL(CAST(F.Value AS VARCHAR(20)), '-') [Actual/Forecast]
FROM
#Forecast F
FULL JOIN #Actuals A
ON A.[Month] = F.[Month]
ORDER BY
[Report Month]
DELETE #Actuals
WHERE [Month] = '20121101'
DELETE #Forecast
WHERE [Month] = '20121201'
DECLARE #LatestActualsPeriod DATE = (SELECT MAX([Month]) FROM #Actuals);
SELECT
[Month] [Report Month]
, CAST(A.Value AS VARCHAR(20)) + ' Actual' [Units]
FROM
#Actuals A
WHERE
A.[Month] < #LatestActualsPeriod
UNION
SELECT
F.[Month] [Report Month]
, ISNULL(CAST(A.Value AS VARCHAR(20)), '-') + '/' + CAST(F.Value AS VARCHAR(20)) + ' Actual/Forecasted'
FROM
#Forecast F
LEFT JOIN #Actuals A
ON A.[Month] = F.[Month]
WHERE
F.[Month] >= #LatestActualsPeriod
P.S.: You put a schema in your answer, but didn't reply to my comment asking for it. If I hadn't looked back at the question again out of curiosity, I wouldn't have know you edited your question.