How to add results of previous columns (SQL) - sql

I am not sure if it is even possible in SQL, will appreciate your response.
I create 4 Case statements to define quantity of sold items (of a specific type).
After that, I want to create another 5th column where I can take an overall sold amount and subtract all those 4 specific types.
I am trying to understand how it can be done in one SQL query. The SUM and division should be done on a row level.
In other words, trying to understand how I can SUM 4 case statements and divide that sum from another column and it all should be in one column.
I hope my explanation makes sense.
p/s I use SQL specifically SSMS.

You've only provided a query fragment so the answer is only a query fragment.
You cannot reference column aliases created in the same level, you can however create your query as a derived table or a CTE, and then the columns are available to the outer query, thus
with t as (
select
p.PDQSLD,
p.[PDITM#] as [Item Number],
Sum(PDQSLD) as [Quantity Sold],
p.PDWEDT as [Week Ending Date],
l.SGDWHS as [Default Ship Warehouse],
p.PDCHN as [Chain Code],
case when p.PDCHN = 'DH' then p.PDQSLD else 0 end as DHSLD,
case when p.PDCHN = 'WM' then p.PDQSLD else 0 end as WMSLD,
case when p.PDCHN = 'MJ' then p.PDQSLD else 0 end as MJSLD,
case when p.PDCHN = 'BJ' then p.PDQSLD else 0 end as BJSLD
from ...
)
select t.*,
PDQSLD - ( DHSLD + WMSLD + MJSLD + BJSLD ) as OTHRSLD
from t
Note that in T-SQL, 'quoted' values are for string literals, if you need to delimit column aliases, use [square brackets].

Related

AND OR SQL operator with multiple records

I have the following query where if brand1/camp1 taken individually, query returns the correct value but if I specify more than one brand or campaigns, it returns some other number and I am not sure what the math is behind that. It is not the total of the two either.
I think it is IN operator that is specifying OR with "," as opposed to what I require it to do which is consider AND
select campaign,
sum(case when campaign in ('camp1', 'camp2') and description in ('brand1', 'brand2') then orders else 0 end) as brand_convs
from data.camp_results
where campaign in ('camp1', 'camp2') and channel='prog' and type='sbc'
group by campaign
having brand_convs > 0
order by brand_convs desc;
Any thoughts?
The problem is in the IN part as you suspected: The two IN operators do not affect eachother in any way, so campaign can be camp1 while description is brand2.
If your DBMS supports multiple columns in an IN statement, you use a single IN statement:
SELECT campaign, SUM(
CASE WHEN (campaign, description) IN (
('camp1', 'brand1'),
('camp2', 'brand2')
) THEN orders ELSE 0 END
) [rest of query...]
If not, you're probably going to have to use ANDs and ORs
SELECT campaign, SUM(
CASE WHEN
(campaign='camp1' AND description='brand1')
OR (campaign='camp2' AND description='brand2')
THEN orders ELSE 0 END
) [rest of query...]

Converting SQL pivot table to T-SQL for Report Builder 3.0

I've been having a spot of bother importing a rather long-winded SQL pivot table dataset into SQL Server Report Builder 3.0 in a format which allows me to add parameter to the report outcome. I understand that this requires the query to be T-SQL friendly
The context is, in case it helps, is that i'm building a report to give a view over various market research panel's eligibility statuses, and i'd like to be able to present a drop down menu to let users flick between panels. So the end #parameter will be on PanelCode / PanelName. It's a composite query:
SELECT
ELT.PanelCode,
ELR.PanelName,
ELR.Year,
ELT.PeriodType,
ELT.PeriodValue,
ELT.TotalPanelists,
ELT.EligiblePanelists,
ELR.TotalEligible,
ELR.TotalVacation,
ELR.TotalExcused,
ELR.TotalInactive,
ELR.TotalConnection,
ELR.TotalCompliance
FROM --the Ineligibility Reason Pivot Table (ELR)
(SELECT
PanelCode,
PanelName,
Year,
PeriodType,
PeriodValue,
Max([Eligible]) as TotalEligible,
Max([Vacation]) as TotalVacation,
Max([Excuse]) as TotalExcused,
Max([Inactive]) as TotalInactive,
Max([Connection]) as TotalConnection,
Max([Compliance]) as TotalCompliance
FROM
(SELECT
PanelCode,
PanelName,
Year,
PeriodType,
PeriodValue,
EligibilityFailureReason,
FROM FullPanellistEligibilityView) FPR
Pivot
(count(EligibilityFailureReason) FOR EligibilityFailureReason IN ([Eligible], [Vacation], [Excuse], [Inactive], [Connection], [Compliance])) AS PVT
WHERE PeriodType <> '4 week period' and Year > 2012
GROUP BY PanelCode, PanelName, PeriodType, Year, PeriodValue) as ELR
, -- And the Eligibility Totals Query, ELT
(
SELECT
PanelCode,
PanelName,
Year,
PeriodType,
PeriodValue,
Count(Poll1s) as TotalPanelists,
Sum(Poll1s) as EligiblePanelists
FROM
(SELECT
PanelCode,
PanelName,
Year
PeriodType,
PeriodValue,
CAST(isEligible as INT) as Poll1s
FROM FullPanellistEligibilityView) FPR
GROUP BY PanelCode, PeriodType, PeriodValue) ELT
WHERE (ELT.PeriodValue=ELR.PeriodValue) and (ELT.PanelCode=ELR.PanelCode)
I've been really struggling to find resources online which suggest how to take larger queries and make them Parameter-able in Report Builder 3. What do I need to add in addition to WHERE PanelName = #PanelName to make this run?
EDIT1: I don't doubt that I've made this query far more complicated than necessary, i'm self-teaching. The schema isn't really necessary as all this data is pulled from one single, already existing view, FullPanellistEligibilityView, sample data, stripped down and mocked up from the view, can be found here
There are two things you need to do in order to set up a data driven parameter selection.
Firstly, you need to create a dataset to populate your parameter drop down menu. This needs to list all the values you want your user to be able to select, in the correct order. This can return a column each for the Label shown to the user and the value passed to the query:
select distinct PanelCode -- Parameter Value
,PanelName -- Parameter Label
from FullPanellistEligibilityView
order by PanelName
Create a Parameter and set the available values to this dataset, with the appropriate column used for the Label and Value properties.
Secondly, you need to add a filter to your dataset. I have taken the liberty of re-writing your query above to use a derived table/common table expression/cte instead of your PIVOT. The code below includes the reference to the SSRS parameter which will insert the 'Value' for the parameter once selected. This code is obviously not tested as I don't have your schema, but the design should be easy enough to understand:
with t
as
(
select PanelCode
,PeriodValue
,count(isEligible) as TotalPanelists -- I'm assuming this is a BIT column, in which case it shouldn't have any null values. If it does, you will need to handle this with count(isnull(isEligible,0))
,Sum(CAST(isEligible as INT)) as EligiblePanelists
from FullPanellistEligibilityView
where PanelCode = #PanelCode -- This will filter your data due to the INNER JOIN below.
group by PanelCode
,PeriodType
,PeriodValue
)
select e.PanelCode
,e.PanelName
,e.Year
,e.PeriodType
,e.PeriodValue
,t.TotalPanelists
,t.EligiblePanelists
,sum(case when e.EligibilityFailureReason = 'Eligible' then 1 else 0 end) as TotalEligible,
,sum(case when e.EligibilityFailureReason = 'Vacation' then 1 else 0 end) as TotalVacation,
,sum(case when e.EligibilityFailureReason = 'Excuse' then 1 else 0 end) as TotalExcused,
,sum(case when e.EligibilityFailureReason = 'Inactive' then 1 else 0 end) as TotalInactive,
,sum(case when e.EligibilityFailureReason = 'Connection' then 1 else 0 end) as TotalConnection,
,sum(case when e.EligibilityFailureReason = 'Compliance' then 1 else 0 end) as TotalCompliance
from FullPanellistEligibilityView e
inner join t
on(e.PanelCode = t.PanelValue
and e.PeriodValue = t.PeriodValue
)
where e.PeriodType <> '4 week period'
and e.Year > 2012
group by e.PanelCode
,e.PanelName
,e.Year
,e.PeriodType
,e.PeriodValue
,t.TotalPanelists
,t.EligiblePanelists

CASE and GROUP BY in SQL

I have been writing a query that allows me to select and count rows for specific product id's and shipment types.
Within this data, what I am now trying to achieve is count which rows have a specific field populated (second member name) and which have not. Then return this as a separate column in my query results.
Here's the query which I have written:
select count(job.JobID) as itemsCount, Lookup_Pack.PackDescription, Lookup_Pack.PackCode, Lookup_Pack.ID, job.shipping,
CASE
WHEN Job.secondMemForename <> '' THEN count(job.JobID)
ELSE 0
END AS [Extra card count]
from job
inner join Lookup_Pack on Lookup_Pack.ID = job.packTypeID
where Lookup_Pack.PackType = 'REN'
AND job.createDate >= '2015-06-01' and Job.createDate <= '2015-06-30'
GROUP BY Lookup_Pack.PackDescription, Lookup_Pack.PackCode, Lookup_Pack.ID, Job.shipping
If I run this query, I get an error returned as I am not grouping by Job.secondMemForename:
[FreeTDS][SQL Server]Column 'job.secondMemForename' is invalid in the
select list because it is not contained in either an aggregate
function or the GROUP BY clause.
although Job.secondMemForename does not form part of the query results.
I have subsequently added this field to the GROUP BY statement, the problem with this is that the data returned for all rows where the CASE applies is un-grouped as the Job.secondMemForename is different for all of them.
Any idea how I can resolve this?
Thanks.
Steeve.
Change Count() to Sum() and add it before CASE
SUM (CASE WHEN Job.secondMemForename <> '' THEN 1 END) AS [Extra card count]

SQL - 2 table values to be grouped by third unconnected value

I want to create a graph that pulls data from 2 user questions generated from within an SQL database.
The issue is that the user questions are stored in the same table, as are the answers. The only connection is that the question string includes a year value, which I extract using the LEFT command so that I output a column called 'YEAR' with a list of integer values running from 2013 to 2038 (25 year period).
I then want to pull the corresponding answers ('forecast' and 'actual') from each 'YEAR' so that I can plot a graph with a couple of values from each year (sorry if this isn't making any sense). The graph should show a forecast line covering the 25 year period with a second line (or column) showing the actual value as it gets populated over the years. I'll then be able to visualise if our actual value is close to our original forecast figures (long term goal!)
CODE BELOW
SELECT CAST((LEFT(F_TASK_ANS.TA_ANS_QUESTION,4)) AS INTEGER) AS YEAR,
-- first select takes left 4 characters of question and outputs value as string then coverts value to whole number.
CAST((CASE WHEN F_TASK_ANS.TA_ANS_QUESTION LIKE '%forecast' THEN F_TASK_ANS.TA_ANS_ANSWER END) AS NUMERIC(9,2)) AS 'FORECAST',
CAST((CASE WHEN F_TASK_ANS.TA_ANS_QUESTION LIKE '%actual' THEN ISNULL(F_TASK_ANS.TA_ANS_ANSWER,0) END) AS NUMERIC(9,2)) AS 'ACTUAL'
-- actual value will be null until filled in each year therefore ISNULL added to replace null with 0.00.
FROM F_TASK_ANS INNER JOIN F_TASKS ON F_TASK_ANS.TA_ANS_FKEY_TA_SEQ = F_TASKS.TA_SEQ
WHERE TA_ANS_ANSWER <> ''
AND (TA_TASK_ID LIKE '%6051' OR TA_TASK_ID LIKE '%6052')
-- The two numbers above refer to separate PPM questions that the user enters a value into
I tried GROUP BY 'YEAR' but I get an
Error: Each GROUP BY expression must contain at least one column that
is not an outer reference - which I assume is because I haven't linked
the 2 tables in any way...
Should I be adding a UNION so the tables are joined?
What I want to see is something like the following output (which I'll graph up later)
YEAR FORECAST ACTUAL
2013 135000 127331
2014 143000 145102
2015 149000 0
2016 158000 0
2017 161000 0
2018... etc
Any help or guidance would be hugely appreciated.
Thanks
Although the syntax is pretty hairy, this seems like a fairly simple query. You are in fact linking your two tables (with the JOIN statement) and you don't need a UNION.
Try something like this (using a common table expression, or CTE, to make the grouping clearer, and changing the syntax for slightly greater clarity):
WITH data
AS (
SELECT YEAR = CAST((LEFT(A.TA_ANS_QUESTION,4)) AS INTEGER)
, FORECAST = CASE WHEN A.TA_ANS_QUESTION LIKE '%forecast'
THEN CONVERT(NUMERIC(9,2), A.TA_ANS_ANSWER)
ELSE CONVERT(NUMERIC(9,2), 0)
END
, ACTUAL = CASE WHEN A.TA_ANS_QUESTION LIKE '%actual'
THEN CONVERT(NUMERIC(9,2), ISNULL(A.TA_ANS_ANSWER,0) )
ELSE CONVERT(NUMERIC(9,2), 0)
END
FROM F_TASK_ANS A
INNER JOIN F_TASKS T
ON A.TA_ANS_FKEY_TA_SEQ = T.TA_SEQ
-- It sounded like you wanted to include the ones where the answer was null. If
-- that's wrong, get rid of the test for NULL.
WHERE (A.TA_ANS_ANSWER <> '' OR A.TA_ANS_ANSWER IS NULL)
AND (TA_TASK_ID LIKE '%6051' OR TA_TASK_ID LIKE '%6052')
)
SELECT YEAR
, FORECAST = SUM(data.Forecast)
, ACTUAL = SUM(data.Actual)
FROM data
GROUP BY YEAR
ORDER BY YEAR
Try something like this ...
SELECT CAST((LEFT(F_TASK_ANS.TA_ANS_QUESTION,4)) AS INT) AS [YEAR]
,SUM(CAST((CASE WHEN F_TASK_ANS.TA_ANS_QUESTION LIKE '%forecast'
THEN F_TASK_ANS.TA_ANS_ANSWER ELSE 0 END) AS NUMERIC(9,2))) AS [FORECAST]
,SUM(CAST((CASE WHEN F_TASK_ANS.TA_ANS_QUESTION LIKE '%actual'
THEN F_TASK_ANS.TA_ANS_ANSWER ELSE 0 END) AS NUMERIC(9,2))) AS [ACTUAL]
FROM F_TASK_ANS INNER JOIN F_TASKS
ON F_TASK_ANS.TA_ANS_FKEY_TA_SEQ = F_TASKS.TA_SEQ
WHERE TA_ANS_ANSWER <> ''
AND (TA_TASK_ID LIKE '%6051' OR TA_TASK_ID LIKE '%6052')
GROUP BY CAST((LEFT(F_TASK_ANS.TA_ANS_QUESTION,4)) AS INT)

How do I add a conditional column to a union query in MS SQL?

In MS SQL is there a way I can add a conditional column to a union query like the one below based on the value of the first column?
basically im trying to add the bin value as a fourth column if the part value is like ('%XX') or have it display 0, so far I have only been able to add another column which mirrors the MAX(part_bins.bin) column if the code doesn't meet the like.
SELECT part_bins.part AS part,
SUM(part_bins.free_stock) AS FREE_STOCK,
MAX(PART_BINS.BIN) AS Bin
FROM part_bins
LEFT OUTER JOIN part ON part_bins.part = part.part
WHERE (part_bins.free_stock > 0)
GROUP BY part_bins.part
UNION
SELECT part.master_part AS part,
SUM(part_bins.free_stock) AS FREE_STOCK,
MAX(PART_BINS.BIN) AS Bin
FROM part_bins
LEFT OUTER JOIN part ON part_bins.part = part.part
WHERE (part_bins.free_stock > 0)
GROUP BY part.master_part
You can use a CASE statement and you have to add it in both queries to add the fourth column like;
--4th Column for both queries, if bin_value is a numeric type
case when part_value like '%XX' then bin_value
else 0 end as fourth_column
--4th Column for both queries, if bin_value is a string type
case when part_value like '%XX' then bin_value
else '0' end as fourth_column