SQL - Counting and Displaying as Columns the Week data - sql

The website I am working on has a SQL report that has been requested to be used as a basis for a new report. The old report takes the usage records of items within the date range and uses it to calculate the total weight with the items being split into their respective item groups so the group's respective total weight usage can be shown as well.
I have cut down the code to its basic form to detail how it currently works
with td as(
select
g.description as "ItemGroup"
,u.itemcode as "ItemCode"
,i.description as "ItemDescription"
,sum(cast(i.weight as real) * usage) as "Weight"
from
usage u
inner join
items i
on i.code = u.itemcode
inner join
groups g
on g.code = i.groupcode
where
date(updatedate) > date(#FROM) and date(updatedate) <= date(#TO)
group by g.description,u.itemcode,i.description)
select 'Detail' as "type",td.* from td
union
select 'Group Total' as "type",itemgroup,null,sum(weight) from td group by itemgroup
union
select 'Total' as "type",'' as itemgroup,null,sum(weight) from td
order by type, itemcode
However the new report they request expands the report to show the individual Week usage totals as columns as well (Week 1,2,...43). Now the problem I see with this is that the SQL would have to know how many weeks are within the Date Range to create columns and then use a where clause equivalent to find the values within that date range for each week. And I don't know if that is even possible with SQL.
I had a look on google and could not find anything related on the matter. I would like to be proven wrong as that would be a powerful tool to have in SQL. Can someone tell me if this is possible?

I don't believe this is possible without writing dynamic SQL. Unless you can rely on there being an upper limit. If you can prepare enough columns to cover your upper limit, you can do something like:
SELECT
g.description,
u.itemcode,
i.description,
SUM(CASE
WHEN (CAST(julianday(date(updatedate)) AS INTEGER) - CAST(julianday(date(#FROM)) AS INTEGER)) / 7 BETWEEN 0 AND 0.9 THEN CAST(i.weight AS REAL) * usage
ELSE 0.0
END) AS Week1Weight,
SUM(CASE
WHEN (CAST(julianday(date(updatedate)) AS INTEGER) - CAST(julianday(date(#FROM)) AS INTEGER)) / 7 BETWEEN 1 AND 1.9 THEN CAST(i.weight AS REAL) * usage
ELSE 0.0
END) AS Week2Weight,
SUM(CASE
WHEN (CAST(julianday(date(updatedate)) AS INTEGER) - CAST(julianday(date(#FROM)) AS INTEGER)) / 7 BETWEEN 2 AND 2.9 THEN CAST(i.weight AS REAL) * usage
ELSE 0.0
END) AS Week3Weight,
And so on...
I'm not familiar with SQLite so I may have gotten some syntax wrong.

you can use strftime function to get the week number from the date column like:
strftime('%W',date(updatedate)) > #FROM
AND
strftime('%W',date(updatedate)) <= #TO

Related

Transposing and summing the top 5 results in Teradata SQL Assistant

I have a query that I converted from Access and is currently working correctly in Teradata SQL Assistant. The data pulled is just a standard table full of all of the data I need.
What I am wondering is: Can something be added to this query that will essentially sum up all of the Exposure values and then only show the top 5 Divisions by greatest to smallest sum (of those Top 5). Also, transposing the data so that my Topics are the left most column.
Here is the working code, details omitted.
SELECT
A.AS_OF_DT
, B.DIVISION
, B.CLASS
, Sum(A.BALANCE/1000000) AS "Bal in MMs"
, Sum(A.EXPOSURE/1000000) AS "Exp in MMs"
, Sum(CASE WHEN A.STATUS = 'NACC' THEN (B.BALANCE/1000000) ELSE 0 END) AS "NPL Bal as MMs"
FROM DB.TABLE1 A LEFT JOIN DB.TABLE2 B ON A.NAICS = B.NAICS_CD
WHERE A.AS_OF_DT= '2017-03-31'
GROUP BY
A.AS_OF_DT,
B.DIVISION,
B.CLASS
ORDER BY SUM (A.EXPOSURE/1000000) DESC
Essentially I want the columns to be the following:
DIVISION|DATE|
Below DIVISION would only be the Top 5 DIVISIONS summarized by EXPOSURE (under DATE)
I can try and clarify if needed. Just let me know.
Thanks!
End result is to have a datapaste I can throw into Excel without the manual work of transposing the data in Excel along with writing formulas to rummage through the 1000's of results of the base query to find summarize the individual Divisions and then picking the top 5 each month.
Thanks!
Shill
To get the 5 top for each division, you can use QUALIFY.
Add this to the end of you query:
QUALIFY ROW_NUMBER() over (PARTITION BY AS_OF_DATE,DIVISION order by (SUM (A.EXPOSURE/1000000))
For your other questions, SQL Assistant isn't much of a presentation tool, it won't do what you are asking for.
If your query already work,
try replacing:
SELECT
By:
SELECT top 10
(line 1)

SQL: Calculate Percentage in new column using another column

I found it hard to describe what I wanted to do in the title, but I will be more specific here.
I have a reasonably long query:
SELECT
/*Amount earned with validation to remove outlying figures*/
Case When SUM(t2.[ActualSalesValue])>=0.01 OR SUM(t2.[ActualSalesValue])<0 Then SUM(t2.[ActualSalesValue]) ELSE 0 END AS 'Amount',
/*Profit earned (is already calculated then input into db, this just pulls that figure*/
SUM(t2.[Profit]) AS 'Profit',
/*Product Type - pulls the product type so that we can sort by product*/
t1.[ucIIProductType] AS 'Product Type',
/*Profit Percentage - This is to calculate the percentage of profit based on the sales price which uses 2 different columns - Case ensures that there are no wild values appearing in the reports as previously experienced*/
Case When SUM(t2.[ActualSalesValue])>=0.01 OR SUM(t2.[ActualSalesValue])<0 THEN (SUM(t2.[Profit])/SUM(t2.[ActualSalesValue])) ELSE 0 END AS 'Profit Percentage',
/*Percentage of Turnover*/
*SUM(t2.[ActualSalesValue])/(Select SUM(t2.[ActualSalesValue]) OVER() FROM [_bvSTTransactionsFull]) AS 'PoT'
/*The join is connect the product type with the profit and the amount*/
FROM [dbo].[StkItem] AS t1
INNER JOIN [dbo].[_bvSTTransactionsFull] AS t2
/*There attirbutes are the links between the tables*/
ON t1.[StockLink]=t2.[AccountLink]
WHERE t2.[TxDate] BETWEEN '1/Aug/2014' AND '31/Aug/2014' AND ISNUMERIC(t2.[Account]) = 1
Group By t1.[ucIIProductType]
The 'Percentage of Turnover' part I am having trouble with - I am trying to calculate the percentage of the Amount based on the total amount - using the same column. So eg: I want to take the Amount value in row 1, then divide it by the total amount of the entire column and then have that value listed in a new column. But I keep getting errors or I Keep getting 1 (because it wants to divide the value by the same value. CAN anyone please advise me on proper syntax for solving this:
/*Percentage of Turnover*/
*SUM(t2.[ActualSalesValue])/(Select SUM(t2.[ActualSalesValue]) OVER() FROM [_bvSTTransactionsFull]) AS 'PoT'
I think you want one of the following:
SUM(t2.[ActualSalesValue])/(Select SUM(t.[ActualSalesValue]) FROM [_bvSTTransactionsFull] t) AS PoT
or:
SUM(t2.[ActualSalesValue])/(SUM(SUM(t2.[ActualSalesValue])) OVER() ) AS PoT
Note: you should use single quotes only for string and date constants, not for column and table names. If you need to escape names, use square braces.

SQL Percercentile Calculation

I have the following query, which even without a ton of data (~3k rows) is still a bit slow to execute, and the logic is a bit over my head - was hoping to get some help optimizing the query or even an alternate methodology:
Select companypartnumber, (PartTotal + IsNull(Cum_Lower_Ranks, 0) ) / Sum(PartTotal) over() * 100 as Cum_PC_Of_Total
FROM PartSalesRankings PSRMain
Left join
(
Select PSRTop.Item_Rank, Sum(PSRBelow.PartTotal) as Cum_Lower_Ranks
from partSalesRankings PSRTop
Left join PartSalesRankings PSRBelow on PSRBelow.Item_Rank < PSRTop.Item_Rank
Group by PSRTop.Item_Rank
) as PSRLowerCums on PSRLowerCums.Item_Rank = PSRMain.Item_Rank
The PartSalesRankings table simply consists of CompanyPartNumber(bigint) which is a part number designation, PartTotal(decimal 38,5) which is the total sales, and Item_Rank(bigint) which is the rank of the item based on total sales.
I'm trying to end up with my parts into categories based on their percentile - so an "A" item would be top 5%, a "B" item would be the next 15%, and "C" items would be the lower 80th percentile. The view I created works fine, it just takes almost three seconds to execute, which for my purposes is quite slow. I narrowed the bottle neck to the above query - any help would be greatly appreciated.
The problem you are having is the calculation of the cumulative sum of PartTotal. If you are using SQL Server 2012, you can do something like:
select (case when ratio <= 0.05 then 'A'
when ratio <= 0.20 then 'B'
else 'C'
end),
t.*
from (select psr.companypartnumber,
(sum(PartTotal) over (order by PartTotal) * 1.0 / sum(PartTotal) over ()) as ratio
FROM PartSalesRankings psr
) t
SQL Server 2012 also have percentile functions and other functions not in earlier versions.
In earlier versions, the question is how to get the cumulative sum efficiently. Your query is probably as good as anything that can be done in one query. Can the cumulative sum be calculated when partSalesRankings is created? Can you use temporary tables?

SQL query to add or subtract values based on another field

I need to calculate the net total of a column-- sounds simple. The problem is that some of the values should be negative, as are marked in a separate column. For example, the table below would yield a result of (4+3-5+2-2 = 2). I've tried doing this with subqueries in the select clause, but it seems unnecessarily complex and difficult to expand when I start adding in analysis for other parts of my table. Any help is much appreciated!
Sign Value
Pos 4
Pos 3
Neg 5
Pos 2
Neg 2
Using a CASE statement should work in most versions of sql:
SELECT SUM( CASE
WHEN t.Sign = 'Pos' THEN t.Value
ELSE t.Value * -1
END
) AS Total
FROM YourTable AS t
Try this:
SELECT SUM(IF(sign = 'Pos', Value, Value * (-1))) as total FROM table
I am adding rows from a single field in a table based on values from another field in the same table using oracle 11g as database and sql developer as user interface.
This works:
SELECT COUNTRY_ID, SUM(
CASE
WHEN ACCOUNT IN 'PTBI' THEN AMOUNT
WHEN ACCOUNT IN 'MLS_ENT' THEN AMOUNT
WHEN ACCOUNT IN 'VAL_ALLOW' THEN AMOUNT
WHEN ACCOUNT IN 'RSC_DEV' THEN AMOUNT * -1
END) AS TI
FROM SAMP_TAX_F4
GROUP BY COUNTRY_ID;
select a= sum(Value) where Sign like 'pos'
select b = sum(Value) where Signe like 'neg'
select total = a-b
this is abit sql-agnostic, since you didnt say which db you are using, but it should be easy to adapat it to any db out there.

Simplify query in H2 database - alternative to TOP X PERCENT

I'm having performance issues with a query and was wondering how to simplify it.
I have a table "Evaluations" (Sample, Category, Jury, Value)
And created some custom functions to get some average values for each sample, so I have this view:
CREATE VIEW Results AS
SELECT Sample,
Category,
IFNULL(COUNT_VALID(Value),0) || ' / ' || COUNT(Value) AS Valid,
CUSTOM_MEAN(Value) AS Mean,
CUSTOM_MEDIAN(Value) AS Median
FROM Evaluations GROUP BY Sample, Category;
Then I want to have another field telling me if each sample is within the 30% of best valued samples of its category. It would be perfect to use TOP(X) PERCENT but it seems H2 doesn't support it so I made a second view that calculates the position in category multiplied by 100, divided by the total count in category and compared to 30:
CREATE VIEW Res AS
SELECT R1.*,
CASE
WHEN (
((SELECT COUNT(*) FROM Results R2
WHERE R2.Category = R1.Category
AND (R2.Mean > R1.Mean OR (R2.Mean = R1.Mean AND R2.Median > R1.Median))) + 1) * 100
/
(SELECT COUNT(*) FROM Results R2 WHERE R2.Category = R1.Category) )
> 30
THEN 'over 30%'
ELSE 'within 30%'
END as 30PERCENT
FROM Results R1 ORDER BY Mean DESC, Median DESC;
This works properly but with just 500 records it takes some time to retrieve the results.
Could someone tell me a more efficient way of constructing this query?
Thanks and regards!