Query that detects difference between accounts/loads from TODAY and YESTERDAY - sql

GOAL: DETECT any difference between yesterday's table loads and today's loads. Each load loads values of data that are associated with bank accounts. So I need a query that returns each individual account that has a difference, with the value in the column name.
I need data from several columns that are located from two different tables. AEI_GFXAccounts and AEI_GFXAccountSTP. Each time the table is loaded, it has a "run_ID" that is incremented by one. So it needs to be compared to MAX(run_id) and MAX(run_id) -1.
I have tried the following queries. All this query does is return all the columns I need. I now need to implement logic that runs these queries WHERE runID = MAX(runID). Then run it again where run_ID = Max(runID) -1. Compare the two tables, show the differences that can be displayed under columns like SELECT AccountBranch WHERE MAX(Run_ID) -1 AS WAS. etc. and another custom named column as 'IS NOW' etc for each column.
SELECT AEI_GFXAccounts.AccountNumber,
AccountBranch,
AccountName,
AccountType,
CostCenter,
TransactionLimit,
ClientName,
DailyCumulativeLimit
FROM AEI_GFXAccounts
JOIN AEI_GFXAccountSTP
ON (AEI_GFXAccounts.feed_id = AEI_GFXAccountSTP.feed_id
and AEI_GFXAccounts.run_id = AEI_GFXAccountSTP.run_id)

I use something similar to this to detect changes for a logging system:
WITH data AS (
SELECT
a.run_id,
a.AccountNumber,
?.AccountBranch,
?.AccountName,
?.AccountType,
?.CostCenter,
?.TransactionLimit,
?.ClientName,
?.DailyCumulativeLimit
FROM
AEI_GFXAccounts a
INNER JOIN AEI_GFXAccountSTP b
ON
a.feed_id = b.feed_id and
a.run_id = b.run_id
),
yest AS (
SELECT * FROM data WHERE run_id = (SELECT MAX(run_id)-1 FROM AEI_GFXAccounts)
),
toda AS (
SELECT * FROM data WHERE run_id = (SELECT MAX(run_id) FROM AEI_GFXAccounts)
)
SELECT
CASE WHEN COALESCE(yest.AccountBranch, 'x') <> COALESCE(toda.AccountBranch, 'x') THEN yest.AccountBranch END as yest_AccountBranch,
CASE WHEN COALESCE(yest.AccountBranch, 'x') <> COALESCE(toda.AccountBranch, 'x') THEN toda.AccountBranch END as toda_AccountBranch,
CASE WHEN COALESCE(yest.AccountName, 'x') <> COALESCE(toda.AccountName, 'x') THEN yest.AccountName END as yest_AccountName,
CASE WHEN COALESCE(yest.AccountName, 'x') <> COALESCE(toda.AccountName, 'x') THEN toda.AccountName END as toda_AccountName,
...
FROM
toda INNER JOIN yest ON toda.accountNumber = yestaccountNumber
Notes:
You didn't say which table some of your columns are from. I've prefixed them with ?. - replace these with a. or as. respectively (always good practice to fully qualify all your column aliases)
When you're repeating out the pattern in the bottom select (above ...) choose data for the COALESCE that will not appear in the column. I'm using COALESCE as a quick way to avoid having to write CASE WHEN a is null and b is not null or b is null and a is not null or a != b, but the comparison fails if accountname (for example) was 'x' yesterday and today it is null, because the null becomes 'x'. If you pick data that will never appear in the column then the check will work out because nulls will be coalesced to something that can never appear in the real data, and hence the <> comparison will work out
If you don't care when a column goes to null today from a value yesterday, or was null yesterday but is a value today, you can ditch the coalesce and literally just do toda.X <> yest.X
New accounts today won't show up until tomorrow. If you want them to show up do toda LEFT JOIN yest .... Of course all their properties will show as new ;)
This query returns all the accounts regardless of whether any changes have been made. If you only want a list of accounts with changes you'll need a where clause that is similar to your case whens:
WHERE
COALESCE(toda.AccountBranch, 'x') <> COALESCE(yest.AccountBranch, 'x') OR
COALESCE(toda.AccountName, 'x') <> COALESCE(yest.AccountName, 'x') OR
...

Do you have a date field? If so you can use Row_Number partitioned by your accounts. Exclude all accounts that have a max of 1 row 'New accounts", and then subtract the Max(rownumber) of each account's load by the Max(rownumber)-1's load. Only return accounts where this returned load is >0.You can also use the lag function to grab the previous accounts load instead of Max(rownumber)-1

Related

Why is the else condition triggered when the when condition is fulfilled?

select t.customer_type, t.balance_no
from test12 t
where t.customer_type=&a1 and t.currency=
(case when &a2 in t.currency then &a2
else 'ALL'
end);
I take a number for customer type and a string for currency from the user and return
the current row. If the currency entered by the user is not available in the table then I should return the row in which the currency value is 'all'. And if the currency entered by the user exists in the table, I need to return the row corresponding to it. But in my code, it doesn't work like that. If the currency entered by the user exists in the table then it returns rows that match both that currency and the else condition.
For example, if a2='USD' then it returns 2 rows, t.currency='USD' and t.currency='ALL',
if a2='ALL' or a value doesn't exist in the table then it works truly, returns 1 row, t.currency='ALL'. What is the problem? Why case-when doesn't work truly?
Your condition and its case expression are evaluated for every row in the table (that matched the customer type, if the optimiser filters that first). It isn't matching both the when and else for any single row.
For the row with 'AZN' neither the when or else result match, so that row is rejected.
For the row with 'USD' the when result is matched, so that row is included.
For the row with 'ALL' the else result is matched, so that row is included.
So if the currency exists you always match both that and 'ALL', and if it doesn't you only match 'ALL'.
The in t.currency suggests you might think that will search the whole table every time, but it is only looking at the data in one row at a time (so should be = not in).
Assuming you only expect a single match you could look for either value, order based on why it matched, and pick the first row:
select t.customer_type, t.balance_no
from test12 t
where t.customer_type = &a1
and t.currency in (&a2, 'ALL')
order by case when t.currency = &a2 then 1 else 2 end
fetch first 1 row only;
Otherwise you can explicitly apply the logic you seems to be expecting, checking the whole table with exists; something like:
select t.customer_type, t.balance_no
from test12 t
where t.customer_type = 1
and (
t.currency = &a2
or
(
t.currency = 'ALL'
and not exists (
select null
from test12
where customer_type = 1
and currency = &a2
)
)
)
or if you want to stick with in:
select t.customer_type, t.balance_no
from test12 t
where t.customer_type = &a1
and t.currency =
case
when &a2 in (select currency from test12 where customer_type = &a1)
then &a2
else 'ALL'
end;
Note that the in is now searching the whole table, via a subquery, not just looking at the current row.
db<>fiddle with fixed values instead of substitution variables.
Incidentally, you might find it easier to quote those, i.e. '&a2', then you don't need to include the quotes when prompted for the currency value.
I think you maybe add one condition (customer_type = &a1) to populate balance_no field's value from currency table.

Not Exists clause -Query

I am using a NOT EXSITS clause in my query and wanted to make sure it was working correctly since I was getting lesser rows than expected.
SELECT DISTINCT offer.courier_uuid,
offer.region_uuid,
offer.offer_time_local,
Cast(scores.acceptance_rate AS DECIMAL(5, 3)) AS acceptance_rate
FROM integrated_delivery.trip_offer_fact offer
JOIN integrated_product.driver_score_v2 scores ON offer.courier_uuid = scores.courier_id
AND offer.region_uuid = scores.region_id
AND offer.business_day BETWEEN date '2019-04-04' AND date '2019-04-07'
AND scores.extract_dt = 20190331
AND NOT EXISTS
(SELECT NULL
FROM source_cassandra_courier_scheduling.assigned_block_by_id_v2 sched
JOIN source_cassandra_delivery.region r ON sched.region_id = r.id
WHERE offer.courier_uuid = sched.courier_id
AND offer.offer_time_local >= date_parse(date_format(AT_TIMEZONE("start",r.time_zone),'%Y-%m-%d %H:%i:%s'),'%Y-%m-%d %H:%i:%s')
AND offer.offer_time_local <= date_parse(date_format(AT_TIMEZONE("end",r.time_zone),'%Y-%m-%d %H:%i:%s'),'%Y-%m-%d %H:%i:%s')
AND element_at(sched.state,-1) = 'ASSIGNED')
ORDER BY 3
Is there anything wrong with my not exists clause? I am only asking since I am getting back lesser rows than expected. The not exists caluse contains a time conversion but i dont think that would affect anything.
I am trying to get all possible ids and their offer times that do NOT EXIST in the scheduled shifts table. I wanted confirm if the way I have the NOT EXISTS clause is correct or if there is something else I would need that would correctly pull all records that exist or not exist in that shed table?

Access query inconsistently treats empty string as null

I have an application that is grabbing data from an Access database. I am seeking the minimum value of a column and the results I am getting back are inconsistent.
Have I run into a feature where Access inconsistently treating an empty string as a null depending on whether I add a filter or not, or is there something wrong with the way I am querying the data?
The column contains one blank value (not null) and several non-blank values that are all identical (about 30 instances of 'QLD'). The query I am using has a filter that involves multiple other tables, so that only the blank value and about half of the 'QLD' values are eligible.
It's probably easier to show the code and the effects rather than describe it. I have created a series of unioned queries which 'should' bring back identical results but do not.
Query:
SELECT 'min(LOC_STATE)' as Category
, min(LOC_STATE) as Result
FROM pay_run, pay_run_employee, employee, department, location
WHERE pr_id = pre_prid
AND em_location = loc_id
AND pre_empnum = em_empnum
AND em_department = dm_id
AND pr_date >= #2/24/2015#
AND pr_date <= #2/24/2016#
UNION ALL
(SELECT TOP 1 'top 1 LOC_STATE'
, LOC_STATE
FROM pay_run, pay_run_employee, employee, department, location
WHERE pr_id = pre_prid
AND em_location = loc_id
AND pre_empnum = em_empnum
AND em_department = dm_id
AND pr_date >= #2/24/2015#
AND pr_date <= #2/24/2016#
ORDER BY LOC_STATE)
UNION ALL
SELECT 'min unfiltered', min(loc_state)
FROM location
UNION ALL
(SELECT TOP 1 'iif is null', iif(loc_state is null, 'a', loc_state)
FROM location
ORDER BY loc_state)
Results:
Category Result
min(LOC_STATE) 'QLD'
top 1 LOC_STATE ''
min unfiltered ''
iif is null ''
If I do a minimum with the filter it brings back 'QLD' and not the empty string. At this stage it is possible that the empty string is not being included because it is treated as a null or the filter removes it.
The second query, which brings back the top 1 state using the filter shows that the empty string is not filtered out, which means that the Min function is ignoring the empty string.
The third query, which gets the minimum of the unfiltered table, brings back the empty string - so the minimum function does not exclude empty strings / treat them as null.
The fourth query, ensures that there is not a null in the empty string position.
My conclusion is that perhaps the inclusion of other tables and filter criteria is causing the empty string value to be treated as a null, but I feel that I must be missing something.
NB: I have a very similar query (date literals altered) that executes against the same data imported into a SQL Server database. It is correctly returning '' for all 4 queries.
Does anyone know why the empty string is ignored by the Min function in the first query?
PS: for those who prefer a query with joins
SELECT 'min(LOC_STATE)' as Category
, min(LOC_STATE) as Result
FROM (((pay_run
INNER JOIN pay_run_employee ON pay_run.pr_id = pay_run_employee.pre_prid)
INNER JOIN employee ON pay_run_employee.pre_empnum = employee.em_empnum)
INNER JOIN department ON employee.em_department = department.dm_id)
INNER JOIN location on employee.em_location = location.loc_id
WHERE
PR_DATE >= #2/24/2015# and
PR_DATE <= #2/24/2016#
union all
(SELECT TOP 1 'TOP 1 LOC_STATE'
, LOC_STATE
FROM (((pay_run
INNER JOIN pay_run_employee ON pay_run.pr_id = pay_run_employee.pre_prid)
INNER JOIN employee ON pay_run_employee.pre_empnum = employee.em_empnum)
INNER JOIN department ON employee.em_department = department.dm_id)
INNER JOIN location on employee.em_location = location.loc_id
WHERE
PR_DATE >= #2/24/2015# and
PR_DATE <= #2/24/2016#
order by LOC_STATE)
union all
select 'min unfiltered', min(loc_state)
from location
This has got nothing to do with corrupt data or unions or joins. The problem can be easily made visible by exectuting following queries in access:
create table testbug (Field1 varchar (255) NULL)
insert into testbug (Field1) values ('a')
insert into testbug (Field1) values ('')
insert into testbug (Field1) values ('c')
select min(field1) from testbug
To my opinion this is a bug in ms-access. When the MIN function in ms-access comes across an empty string ('') it forgets all the values he has come across and returns the minimum value from all the values below the empty string. (in my simple example only value 'c')

How to add Order by in sql query

update Room set Status = case
when Room_Rev.In_DateTime IS NOT NULL and Room_Rev.Out_DateTime IS NULL
then 'U'
when Room_Rev.In_DateTime IS NOT NULL and Room_Rev.Out_DateTime IS NOT NULL
then 'A'
when Room.Status!='R' and Room.Status!='U' and Room.Status!='A'
then Room.Status
else 'R'
end
FROM Room JOIN Room_Rev
ON Room.Room_ID=Room_Rev.Room_ID
and
((Room_Rev.Start_Date >= '2015-03-22' and Room_Rev.End_Date <= '2015-03-22')
OR
(Room_Rev.Start_Date<= '2015-03-22' and Room_Rev.End_Date> '2015-03-22')
OR
(Room_Rev.Start_Date< '2015-03-22' and Room_Rev.End_Date>= '2015-03-22'))
How to add order by Rev_ID desc in the query?
There are two table which is Room and Room_Rev,
they are one to many relationship
The last two row ROM0006 already fill the In_DateTime and Out_DateTime,
thus it regard check out,
and the last row insert new reservation,
the In_DateTime is null
thus i need the query return 'R' (Reserved status)
As one of the possible solutions I suggest a nested query instead of a join in UPDATE statement. The logic of the update is not completely clear to me, so I leave the final update for OP to correct order of sorting (Note I used top 1 and order by room_ID in the nested SELECT statement). However, this approach allows to handle all usual techniques applicable for a SELECT.
update Room set Status = (select TOP 1 case
when Room_Rev.In_DateTime IS NOT NULL and Room_Rev.Out_DateTime IS NULL
then 'U'
when Room_Rev.In_DateTime IS NOT NULL and Room_Rev.Out_DateTime IS NOT NULL
then 'A'
when Room.Status!='R' and Room.Status!='U' and Room.Status!='A'
then Room.Status
else 'R'
end
FROM Room_Rev
WHERE Room.Room_ID=Room_Rev.Room_ID
and
((Room_Rev.Start_Date >= '2015-03-22' and Room_Rev.End_Date <= '2015-03-22')
OR
(Room_Rev.Start_Date<= '2015-03-22' and Room_Rev.End_Date> '2015-03-22')
OR
(Room_Rev.Start_Date< '2015-03-22' and Room_Rev.End_Date>= '2015-03-22'))
ORDER BY Room_Rev.Room_Id
)
PS. As a piece of advise I still assume that such approach is not valid. It prevents proper normalization of data. You'd rather have this information always queried dynamically when required, instead of writing static value to ROOM.status

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.