SQL in Access, subquery pairing issue - sql

I wrote an SQL statement in Access that looks for rows of data with the same value in their 'Code' column and then looks at those rows with the same 'Code' value and finds rows with opposite 'Money Amt' column values (for example one row would have a value of 200 and another would have -200) a column labeled line is then populated with the number '999' when pairs of these opposite values with the same code are found.
The problem is that some of my rows are pairing in odd numbered groups instead of by pairs of 2. I want there to be a 1:1 relationship for the opposing values so that they essentially negate each other.
Here is the code I used to get the result so far.
UPDATE [Actual Debt]
SET LineItem = 999
WHERE EXISTS
(SELECT * FROM [Actual Debt] as ad2
WHERE ad2.Code = [Actual Debt].Code
AND ad2.[Money Amt] = - [Actual Debt].[Money Amt]);
In the picture you can see that in the first 4 rows the code populated 2 pairs of opposing 'Money Amt' values as intended. But, in the bottom 3 rows there should have only been 1 pair of '999', but instead it has populated an odd number of rows.
How do I get the subquery to only populate the Line field for opposing pairs.
Image from before I put in the current code
Thank you in advance.

Now Gordon I do believe this can be done in MS Access. You can do just about everything in Access that you can do in other database its just a matter of figuring out the how. However in this particular case there seems to be a problem with the underlying data that is causing the current issue and without seeing the entire source table it is very hard to determine what that error is. Still I would venture to guess that if you ran your sub-Query by itself (as I show below) that you would get the data as I show below. Try running just the SELECT part of your query and see what you get.
SELECT *
FROM ActualDebt AS Ad2
WHERE Ad2.Code = ActualDebt.Code
AND Ad2.MoneyAmt = ((-1) * ActualDebt.MoneyAmt);
Most likely the Query Results are as follows based on what I see in your table
Code | MoneyAmt
MHT .3
MHT .3
MHT -.3
MHT -.3
RLO .4
RLO .6
MQR .9
MQR -.9
MQR .9
So Gordon if what I have outlined above, is the issue, then yes you will need to provide more information in order for a solution to be determine because programmatically right now there would be no way to differentiate between the first MQR .9 and the other MQR .9 -- which seems to be the source of the issue
I will keep an eye on this post and if you edit your question then I will edit this answer to help.

Related

Calculated Field in subreport

TL:DR I want a whole column of SQL equivalent of excel's COUNTIFS() Function.
I'm still quite new to MS Access, but I'm quite proficient with Excel. I've inherited an Access Database that tracks reasons for delays in a large logistics group, and am trying to build a report showing which reasons come up most.
So, I can output an SQL report showing all the reasons (they're in a table called 'Reasons', so that bit's easy). What I want is a calculated field next to that column, showing how many times each reason has been cited on the [Master Data] Table (field called 'Lateness Reason') in a given date range. And for double extra bonus points, the percentage they come up would be extremely handy.
I've looked online and found COUNTIFS equivalents for a single set of criteria, but in this case I want it calculating on each row of a report. I've also tried a few things myself, but the closest I could figure was:
SELECT Reasons.Reason, Count([Master Data].[ID]) AS Num
FROM Reasons INNER JOIN [Master Data] ON Reasons.[ID] = [Master Data].[Lateness Reason]
WHERE ((([Master Data].[Lateness Reason])=[Reasons].[Reason]));
which has incorrect syntax and possibly a few other problems (that WHERE clause has to apply equally to all lines, doesn't it?). My only other option might be to do a separate calculation for each line and 'union' them together, but that is likely to cause other problems in the future if more reasons get added (and there are quite a lot already).
Firstly, Is it Possible?
Secondly, If so, How??
Many Thanks in advance
EDIT: In response to comments, table structure is as follows;
Table "Reasons" has two columns; "Reason" and "ID"
Table "Master Data" has many columns, the ones I'd be bothered about are "Date", "ID" and "Lateness Reason" (Lateness reason is equivalent to an ID from table 'Reasons')
Table Reasons
ID Reason
___________________
1 | Stock Shortage
2 | Courier Problems
etc | etc
Master Data
ID Date Reason
__________________
1 | 01/01/1980 | 2
2 | 03/05/2020 | 2
etc
This is how I think your query should look like based on the information you provided.
SELECT Reasons.Reason, Count([Master Data].ID) AS Num
FROM Reasons INNER JOIN [Master Data] ON Reasons.ID = [Master Data].[Lateness Reason]
WHERE ((([Master Data].Date) Between [BeginDate] And [EndDate]))
GROUP BY Reasons.Reason;
Replace [BeginDate] and [EndDate] if you're using form control references.
As for the percentages based on the total, you can use text boxes as #June7 has suggested:
Use a total count in the header or footer of the report =Count(*) and then in the detail add a text box with the following control source: =[Num]/Count(*), where Num is the name of the textbox in your report which holds the counted values per reason. In this case, the control source for Num will be Num based on the query given.
Just a side note, naming your field Date is not advised since it's a reserved keyword in MS Access. It can cause unintended issues along the road.

Create and define functions to apply to different segments/filters that exist (Instead of doing one very long SQL query)

I am trying to come up with some arithmetic calculations for some survey data. I want to do these calculations for a number of segments and want to figure out how to do it without writing numerous SELECT statements.
This is what I have so far:
FACT table. This tables holds survey data at a respondent level - for example, if a survey had 10 questions, this table will have 11 columns: a column to identify the respondent_ID and 10 other columns to identify the responses to those questions.
DIMENSION table. This table segments we want to view the survey data by at a respondent level - for example, if we want to view survey responses by membership_status and age_bracket, this table will have 3 columns: a column to identify the respondent_ID, and two columns to identify membership_status and age_bracket.
OUTPUT.
I want to get aggregate calculations to summarizes the responses to the survey overall and to each question. I also want to be able to get this information for all possible segments that exist in the DIMENSIONS table.
I can do the query below, however I'll need to do this for every segment:
SELECT
COUNT(DISTINCT(CASE WHEN f.QUESTION_1 IN ('8', '9', '10') THEN f.RESPONDENT_ID END))*1.0 / COUNT(DISTINCT(CASE WHEN f.QUESTION_1 IS NOT NULL THEN f.RESPONDENT_ID END))*1.0 AS CSAT_1
FROM FACT f
JOIN DIMENSION d ON f.RESPONDENT_ID = d.RESPONDENT_ID
WHERE d.MEMBERSHIP_STATUS = 'ACTIVE'
The calculation above gives us something called a top 3 box. That is just one calculation, I will need to do many of them. Additionally, ever calculation will need to be done for each segment. In order to get a calculation for nonactive members, I would need to run another query and set d.MEMBERSHIP_STATUS = 'INACTIVE' and I would need to run another query with no filter, to get the overall calculation.
Is there a way I could store all my arithmetic calculations needed in my output as a function (maybe in a temp table or something) - my thought is that it'll be better to set the functions somewhere, and then when I need to calculate the output, I would some how call the function to do all the calculations I need, and give me all the calculations for every segment I have?
I can't fully envision how to get there, or if this is even a good solution, so guidance and detailed SQL code would be extremely helpful.Examples please!

Failed UPDATE with CASE

I'm trying to write a query which will update reorder_level based on how much of an item was sold within a particular time period.
with a as (select invoice_itemized.itemnum, inventory.itemname,
sum(invoice_itemized.quantity) as sold
from invoice_itemized
join inventory on invoice_itemized.itemnum=inventory.itemnum and
inventory.vendor_number='COR' and inventory.dept_id='cigs'
join invoice_totals on
invoice_itemized.invoice_number=invoice_totals.invoice_number and
invoice_totals.datetime>=dateadd(month,-1,getdate())
group by invoice_itemized.itemnum, inventory.itemname)
update inventory
set reorder_level = case when a.sold/numpervencase>=5 then 30
when a.sold/numpervencase>=2 then 20
when a.sold/numpervencase>=1 then 5
else 1 end,
reorder_quantity = 1
from a
join inventory_vendors on a.itemnum=inventory_vendors.itemnum
Replacing the update with a select performs entirely as expected, returning proper results from the case and selecting 94 rows.
with the update in place, all of the areas affected by the update (6758) got set to 1.
Run this, and eyeball the results:
with a as (select invoice_itemized.itemnum, inventory.itemname,
sum(invoice_itemized.quantity) as sold
from invoice_itemized
join inventory on invoice_itemized.itemnum=inventory.itemnum and
inventory.vendor_number='COR' and inventory.dept_id='cigs'
join invoice_totals on
invoice_itemized.invoice_number=invoice_totals.invoice_number and
invoice_totals.datetime>=dateadd(month,-1,getdate())
group by invoice_itemized.itemnum, inventory.itemname)
select a.sold, numpervencase, a.sold/numpervencase,
case
when a.sold/numpervencase>=5 then 30
when a.sold/numpervencase>=2 then 20
when a.sold/numpervencase>=1 then 5
else 1
end,
*
from a
join inventory_vendors on a.itemnum=inventory_vendors.itemnum
Always a good idea to select before update to check that data ends up as you expect
all of the areas affected by the update got set to 1
I put the raw ingredients into the query above; see if the sums worked out as expected. You might need to cast one of the operands to something with decimal places:
1/2 = 0
1.0/2 = 0.5
And it updated far more rows than i was expecting
Every row that comes out of that select will be updated. Identify the rows you don't want to update and put a where clause in to remove them
Am i overthinking this?
Undertesting, probably
Do I even need the cte?
Makes it easier to represent, but no- you could get the same result by pasting the contents of the cte in as a subquery.. it's what the db does (effectively) anyway
Do i have my from statement in the wrong place?
We don't know what result you're after so that one is impossible to answe beyond "doing so would probably generate a syntax error, so.. no"
The actual problem seems to be
your case when is always going to ELSE, find out why
your cte selects too many rows (I couldn't tell if the number you posted was the number you got or the number you were expecting but it's pretty moot without example data), find out why
Solved. When I added another join to the update it worked correctly. i had to add join inventory on inventory_vendors.itemnum=inventory.itemnum

MS SQL 2000 - How to efficiently walk through a set of previous records and process them in groups. Large table

I'd like to consult one thing. I have table in DB. It has 2 columns and looks like this:
Name...bilance
Jane...+3
Jane...-5
Jane...0
Jane...-8
Jane...-2
Paul...-1
Paul...2
Paul....9
Paul...1
...
I have to walk through this table and if I find record with different "name" (than was on previous row) I process all rows with the previous "name". (If I step on the first Paul row I process all Jane rows)
The processing goes like this:
Now I work only with Jane records and walk through them one by one. On each record I stop and compare it with all previous Jane rows one by one.
The task is to sumarize "bilance" column (in the scope of actual person) if they have different signs
Summary:
I loop through this table in 3 levels paralelly (nested loops)
1st level = search for changes of "name" column
2nd level = if change was found, get all rows with previous "name" and walk through them
3rd level = on each row stop and walk through all previous rows with current "name"
Can this be solved only using CURSOR and FETCHING, or is there some smoother solution?
My real table has 30 000 rows and 1500 people and If I do the logic in PHP, it takes long minutes and than timeouts. So I would like to rewrite it to MS SQL 2000 (no other DB is allowed). Are cursors fast solution or is it better to use something else?
Thank you for your opinions.
UPDATE:
There are lots of questions about my "summarization". Problem is a little bit more difficult than I explained. I simplified it just to describe my algorithm.
Each row of my table contains much more columns. The most important is month. That's why there are more rows for each person. Each is for different month.
"Bilances" are "working overtimes" and "arrear hours" of workers. And I need to sumarize + and - bilances to neutralize them using values from previous months. I want to have as many zeroes as possible. All the table must stay as it is, just bilances must be changed to zeroes.
Example:
Row (Jane -5) will be summarized with row (Jane +3). Instead of 3 I will get 0 and instead of -5 I will get -2. Because I used this -5 to reduce +3.
Next row (Jane 0) won't be affected
Next row (Jane -8) can not be used, because all previous bilances are negative
etc.
You can sum all the values per name using a single SQL statement:
select
name,
sum(bilance) as bilance_sum
from
my_table
group by
name
order by
name
On the face of it, it sounds like this should do what you want:
select Name, sum(bilance)
from table
group by Name
order by Name
If not, you might need to elaborate on how the Names are sorted and what you mean by "summarize".
I'm not sure what you mean by this line... "The task is to sumarize "bilance" column (in the scope of actual person) if they have different signs".
But, it may be possible to use a group by query to get a lot of what you need.
select name, case when bilance < 0 then 'negative' when bilance >= 0 then 'positive', count(*)
from table
group by name, bilance
That might not be perfect syntax for the case statement, but it should get you really close.

Update query (access & vb.net)

QUESTION 1
I have to update some fields of a table of access according to the value of parameter. If this paramter is "true" I need to update.
Table
idInvoice
price
percentageTax1
tax1
percentageTax2
tax2
total
Example values:
idinvoice: 12300
price: 100 €
percentageTax1: 10 %
tax1= 10€
percentageTax2: 5 %
tax2: 5€
total: 115 € (result: 100€+10€+5€)
Ok. If the parameter on that I have commented before is "true" I must update the percentages and then update the total. I need to replace the "old" percentages by new percent.
Ok I can do it in 3 queries:
update invocies set percentageTax1=20,tax1=price *(percentageTax1/100) where idInvoice=#number and percentageTax1=10
update invocies set percentageTax2=7,tax2=price *(percentageTax2/100) where idInvoice=#number and percentageTax2=5
update invocies set total=price+tax1+tax2 where idInvoice=#number
. But my question is:
is there any an alternative to do this in 1 query?
QUESTION 2
Other question about update.
If I have 2 linked tables
EXAMPLE TABLE INVOICE
idInvoice
total
EXAMPLE TABLE DETAIL
idInovice
Line
price
The "total" field is the total sum of the field "price" of the table "detail"
how it would be the "query" to update "total" field?
TOTAL field need to be stored in database
SOLUTION QUESTION 2:
is possible if I do this query. Example.
update table1 INNER JOIN table2 ON table1.id=table2.id set table1.field1=table2.fieldX
It is more common to pull out the invoice you want to work on, do the maths in the code to update the values, then put all the updated values back in the database in one go.
There are a thousand different ways of doing this!
possibly one place to start is http://code.msdn.microsoft.com/eisk
or here
http://www.asp.net/data-access/tutorials/an-overview-of-inserting-updating-and-deleting-data-vb
In answer to Question two.
Unless there is some special need to store the calculated result in the database I would just calculate it each time I wanted to do anything with the total.
This may not scale if you are writing StackOverflow, but for normal use it saves a place where data can slip out of sync.
Get rid of the calculated fields if possible.
update invocies
set percentageTax1 = IIf(percentageTax1=10, 20, percentagetax1)
, percentageTax2 = IIf(percentageTax2=5, 7, percentageTax2)
where idInvoice=#number AND (percentageTax1=10 OR percentageTax2=5)