Case, Select conditional requirments - sql

Operating in an Excel query, I need a conditional statement that will read one field, and based on that value, set another field to a minus (or not).
My SQl code is as follows:
SELECT "_bvSTTransactionsFull".txdate,
SUM("_bvSTTransactionsFull".debit) AS 'TOTALDebit',
SUM("_bvSTTransactionsFull".credit) AS 'TOTALCredit',
SUM("_bvSTTransactionsFull".tax_amount) AS 'TOTALTax_Amount',
SUM("_bvSTTransactionsFull".VALUE) AS 'TOTALValue',
SUM("_bvSTTransactionsFull".actualvalue) AS 'TOTALActualValue',
SUM("_bvSTTransactionsFull".actualsalesvalue) AS 'TOTALActualSalesValue',
SUM("_bvSTTransactionsFull".profit) AS 'TOTALProfit'
FROM sqlschema.dbo."_bvSTTransactionsFull" "_bvSTTransactionsFull"
WHERE ( "_bvSTTransactionsFull".txdate >=?
AND "_bvSTTransactionsFull".txdate <=? )
GROUP BY "_bvSTTransactionsFull".txdate,
"_bvSTTransactionsFull".description
HAVING ( "_bvSTTransactionsFull".description LIKE 'POS Sale' )
OR ( "_bvSTTransactionsFull".description LIKE 'POS Return' )
ORDER BY "_bvSTTransactionsFull".txdate
I need the select query to look at a field named "ActualQuantity" (in the table "_bvSTTransactionsFull") and if this field is <0 , then Tax_Amount = -(Tax_Amount), or if ActualQuantity >=0, then Tax_Amount = Tax_Amount.
Please note the query is "summed", so I assume this conditional aspect needs to be handled before summation takes place. The query summates approximately 100 000 records into daily totals.

SELECT "_bvSTTransactionsFull".txdate,
SUM("_bvSTTransactionsFull".debit) AS 'TOTALDebit',
SUM("_bvSTTransactionsFull".credit) AS 'TOTALCredit',
SUM(case when "_bvSTTransactionsFull".ActualQuantity >= 0
then "_bvSTTransactionsFull".tax_amount
else - "_bvSTTransactionsFull".tax_amount
end) AS 'TOTALTax_Amount',
SUM("_bvSTTransactionsFull".VALUE) AS 'TOTALValue',
SUM("_bvSTTransactionsFull".actualvalue) AS 'TOTALActualValue',
SUM("_bvSTTransactionsFull".actualsalesvalue) AS 'TOTALActualSalesValue',
SUM("_bvSTTransactionsFull".profit) AS 'TOTALProfit'
FROM sqlschema.dbo."_bvSTTransactionsFull" "_bvSTTransactionsFull"
WHERE ( "_bvSTTransactionsFull".txdate >=?
AND "_bvSTTransactionsFull".txdate <=? )
GROUP BY "_bvSTTransactionsFull".txdate,
"_bvSTTransactionsFull".description
HAVING ( "_bvSTTransactionsFull".description LIKE 'POS Sale' )
OR ( "_bvSTTransactionsFull".description LIKE 'POS Return' )
ORDER BY "_bvSTTransactionsFull".txdate
If ActualQuantity might be zero but taxAmount in the same row is zero too you can use sign function to change sign of tax_amount:
sign(ActualQuantity) * tax_amount
UPDATE:
So I gather that MS Query has problems using parameters in a query that cannot be displayed graphically. Workaround is to replace parameters with some constants, save workbook as XML and change constants to "?". There is a link showing VBA code which does replacement on-site, but you will have to figure out how it works as I don't know VBA that much.
UPDATE 2:
On the other hand the easiest way out is to create view in your database.

Related

SQL Group by CASE result

I have a simple SQL query on IBM DB2. I'm trying to run something as below:
select case when a.custID = 42285 then 'Credit' when a.unitID <> '' then 'Sales' when a.unitID = '' then 'Refund'
else a.unitID end TYPE, sum(a.value) as Total from transactions a
group by a.custID, a.unitID
This query runs, however I have a problem with group by a.custID - I'd prefer not to have this, but the query won't run unless it's present. I'd want to run the group by function based on the result of the CASE function, not the condition pool behind it. So, I'm looking something like:
group by TYPE
However adding group by TYPE reports an error message "Column or global variable TYPE not found". Also removing a.custID from group section reports "Column custID or expression in SELECT list not valid"
Is this going to be possible at all or do I need to review my CASE function and avoid using the custID column since at the moment I'm getting a grouping also based on custID column, even though it's not present in SELECT.
I understand why the grouping works as it does, I'm just wondering if it's possible to get rid of the custID grouping, but still maintain it within CASE function.
If you want terseness of code, you could use a subquery here:
SELECT TYPE, SUM(value) AS Total
FROM
(
SELECT CASE WHEN a.custID = 42285 THEN 'Credit'
WHEN a.unitID <> '' THEN 'Sales'
WHEN a.unitID = '' THEN 'Refund'
ELSE a.unitID END TYPE,
value
FROM transactions a
) t
GROUP BY TYPE;
The alternative to this would be to just repeat the CASE expression in the GROUP BY clause, which is ugly, but should still work. Note that some databases (e.g. MySQL) have overloaded GROUP BY and do allow aliases to be used in at least some cases.

Nested Case Statements in Oracle

So, I'm trying to run a SQL Statement to select and entire DB for upload in an ETL process, but I want to create a calculated column for the number of days between a ticket opening and being closed.
The IF-THEN logic is like this:
IF the department is Grounds Maintenance, and there's a foreign key match with a second table, and there's specific task type, then use formula A
ELSE
IF INCIDENT_RESOLVED_DATE IS NULL, then use formula B
ELSE use formula C
I think my CASE logic is solid, but it keeps bringing me back the same row over and over again. This tells me I'm missing something. I'm almost positive it's something to do with the WHEN statement on the first part of the CASE statement, but if I knew, I wouldn't be asking.
SELECT
a.*
, a.REPORTED_DATE
, a.CLOSE_DATE
, a.INCIDENT_RESOLVED_DATE
, CASE
WHEN DEPARTMENT = 'Grounds Maintenance'
AND a.INCIDENT_ID = b.SOURCE_OBJECT_ID
AND b.TASK_TYPE_ID = '11501'
THEN (to_date(b.ACTUAL_END_DATE, 'DD-MM-YYYY') - to_date(a.REPORTED_DATE, 'DD-MM-YYYY'))
ELSE CASE
WHEN a.INCIDENT_RESOLVED_DATE IS NULL THEN (to_date(a.CLOSE_DATE, 'DD-MM-YYYY') - to_date(a.REPORTED_DATE, 'DD-MM-YYYY'))
ELSE (to_date(a.INCIDENT_RESOLVED_DATE, 'DD-MM-YYYY') - to_date(a.REPORTED_DATE, 'DD-MM-YYYY'))END
END
AS
DAYS_TO_RESOLVE
FROM
CMEM_CS_SERVICE_REQUESTS a, jtf_tasks_b b
WHERE
EXTRACT(YEAR FROM a.REPORTED_DATE) > 2009;
Thoughts?

SubQuery Aggregates in ActiveRecord

I'm trying to avoid using straight up SQL in my Rails app, but need to do a quite large version of this:
SELECT ds.product_id,
( SELECT SUM(units) FROM daily_sales WHERE (date BETWEEN '2015-01-01' AND '2015-01-08') AND service_type = 1 ) as wk1,
( SELECT SUM(units) FROM daily_sales WHERE (date BETWEEN '2015-01-09' AND '2015-01-16') AND service_type = 1 ) as wk2
FROM daily_sales as ds group by ds.product_id
I'm sure it can be done, but i'm struggling to write this as an active record statement. Can anyone help?
If you must do this in a single query, you'll need to write some SQL for the CASE statements. The following is what you need:
ranges = [ # ordered array of all your date-ranges
Date.new(2015, 1, 1)..Date.new(2015, 1, 8),
Date.new(2015, 1, 9)..Date.new(2015, 1, 16)
]
overall_range = (ranges.first.min)..(ranges.last.max)
grouping_sub_str = \
ranges.map.with_index do |range, i|
"WHEN (date BETWEEN '#{range.min}' AND '#{range.max}') THEN 'week#{i}'"
end.join(' ')
grouping_condition = "CASE #{grouping_sub_str} END"
grouping_columns = ['product_id', grouping_condition]
DailySale.where(date: overall_range).group(grouping_columns).sum(:units)
That will produce a hash with array keys and numeric values. A key will be of the form [product_id, 'week1'] and the value will be the corresponding sum of units for that week.
Simplify your SQL to the following and try converting it..
SELECT ds.product_id,
, SUM(CASE WHEN date BETWEEN '2015-01-01' AND '2015-01-08' AND service_type = 1
THEN units
END) WK1
, SUM(CASE WHEN date BETWEEN '2015-01-09' AND '2015-01-16' AND service_type = 1
THEN units
END) WK2
FROM daily_sales as ds
group by ds.product_id
Every rail developer sooner or later hits his/her head against the walls of Active Record query interface just to find the solution in Arel.
Arel gives you the flexibility that you need in creating your query without using loops, etc. I am not going to give runnable code rather some hints how to do it yourself:
We are going to use arel_tables to create our query. For a model called for example Product, getting the Arel table is as easy as products = Product.arel_table
Getting sum of a column is like daily_sales.project(daily_sales[:units].count).where(daily_sales[:date].gt(BEGIN_DATE).where(daily_sales[:date].lt(END_DATE). You can chain as many wheres as you want and it will be translated into SQL ANDs.
Since we need to have multiple sums in our end result you need to make use of Common Table Expressions(CTE). Take a look at docs and this answer for more info on this.
You can use those CTEs from step 3 in combination with group and you are done!

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)

multiple count(distinct)

I get an error unless I remove one of the count(distinct ...). Can someone tell me why and how to fix it?
I'm in vfp. iif([condition],[if true],[else]) is equivalent to case when
SELECT * FROM dpgift where !nocalc AND rectype = "G" AND sol = "EM112" INTO CURSOR cGift
SELECT
list_code,
count(distinct iif(language != 'F' AND renew = '0' AND type = 'IN',donor,0)) as d_Count_E_New_Indiv,
count(distinct iif(language = 'F' AND renew = '0' AND type = 'IN',donor,0)) as d_Count_F_New_Indiv /*it works if i remove this*/
FROM cGift gift
LEFT JOIN
(select didnumb, language, type from dp) d
on cast(gift.donor as i) = cast(d.didnumb as i)
GROUP BY list_code
ORDER by list_code
edit:
apparently, you can't use multiple distinct commands on the same level. Any way around this?
VFP does NOT support two "DISTINCT" clauses in the same query... PERIOD... I've even tested on a simple table of my own, DIRECTLY from within VFP such as
select count( distinct Col1 ) as Cnt1, count( distinct col2 ) as Cnt2 from MyTable
causes a crash. I don't know why you are trying to do DISTINCT as you are just testing a condition... I more accurately appears you just want a COUNT of entries per each category of criteria instead of actually DISTINCT
Because you are not "alias.field" referencing your columns in your query, I don't know which column is the basis of what. However, to help handle your DISTINCT, and it appears you are running from WITHIN a VFP app as you are using the "INTO CURSOR" clause (which would not be associated with any OleDB .net development), I would pre-query and group those criteria, something like...
select list_code,
donor,
max( iif( language != 'F' and renew = '0' and type = 'IN', 1, 0 )) as EQualified,
max( iif( language = 'F' and renew = '0' and type = 'IN', 1, 0 )) as FQualified
from
list_code
group by
list_code,
donor
into
cursor cGroupedByDonor
so the above will ONLY get a count of 1 per donor per list code, no matter how many records that qualify. In addition, if one record as an "F" and another does NOT, then you'll have a value of 1 in EACH of the columns... Then you can do something like...
select
list_code,
sum( EQualified ) as DistEQualified,
sum( FQualified ) as DistFQualified
from
cGroupedByDonor
group by
list_code
into
cursor cDistinctByListCode
then run from that...
You can try using either another derived table or two to do the calculations you need, or using projections (queries in the field list). Without seeing the schema, it's hard to know which one will work for you.