Select row each time value changes based on date order - sql

Good Day Hopefully I can explain my problem well enough. I do not have any sample query as I can not get anything to work. But my problem is I have a table that contains a list of accounts and their status and a date that a change occurred on the account. I need to pull the account number and each time the status changes along with the first date it changed. I have tried using rank and min(date) and max(date); the problem I am running into is the account can go back and forth between statuses and I need each time it changes. Also, a new row could be in the table with the same account number and status but the update date is different.
This is sample data:
abc C50 1/20/2022
abc C50 1/21/2022
abc C09 2/20/2022
abc C50 3/1/2022
def A54 1/20/2022
def A26 1/21/2022
def A26 2/20/2022
def A54 3/1/2022
As you can see account abc has 3 instances of C50 and one instance of C09 for my results I would expect to see, the earliest of the first two C50s, then the C09 and then next C50, since its a new status change.
abc C50 1/20/2022
abc C09 2/20/2022
abc C50 3/1/2022
For the second account def; I would expect to see the first A54, the first A26, then the next A54 on the 1st as it is a new instance.
def A54 1/20/2022
def A26 1/21/2022
def A54 3/1/2022

IMO With this kind of problem I find it better to think of a different kind of model. The query you are asking for is not easy to write (if at all possible) and would be difficult to maintain moving forward if your underlying structure changes
So what options do you have?
Option 1 Create a summary table of ACTUAL changes. Think of this table of a log/audit table where you only record a change when it happens. Then you can simply read from this table and out put results
[ChangeTable]
ABC C50 1/20/2022
ABC C09 2/20/2022
ABC C50 3/1/2022
Option 2
Create a revision column on your main table. You would start with 1 for the first instance and only increase the revision if a change happens. e.g.
REVISION
abc C50 1/20/2022 0 First instance
abc C50 1/21/2022 -1 or null if you prefer indicates no change
abc C09 2/20/2022 1 First change
abc C50 3/1/2022 2 Second change
Then you can get all revisions for an account that is greater than ZERO and ordered by revision (or desc so newest on top). You can also use this column so you can show 1st change, 2nd change etc.
Both options you would need to check for changes before you insert and append/update where appropiate.
Now you will probably say this is data I get and I cant modify the application. If so then you may be able to write a small application that is scheduled to do this work for you on a hourly/nightly basis

Related

Changes to table not designed for SQL

I am supposed to do some changes to an enormous CSV file based on a different file. Therefore I chose to do it in SQL but after further consideration I am not sure how to proceed..
In the 1st table I have a list of contracts. Columns represent some segments the contract belongs to and some products that can be linked to the contract (example in the table below).
Here contract no. 1234 belongs to segments X1 and Y2. There is no product number 1 linked to it, but it has product number 2 linked to it. The product originaly ends on the 1st of January 2030.
cont_n|date|segment_1|segment_2|..|prod_1|date_prod_1|product_2|date_product_2|..
1234 |3011| X1 | Y2 |..| | |YES |01/01/2030 |..
The 2nd file is a list of combinations of segments and an indication how the "date" columns should be adjusted. The example shows following situation - if there is prod_2 linked to the contract which belongs to groups X1 and Y2, end the prod_2 this year. I need this result to alter table no. 1.
prod_no|segment_1|segment_2|result
prod_2 | X1 | Y2 | end the product on anniversary
Ergo I need to get to the result:
cont_n|date|segment_1|segment_2|..|prod_1|date_prod_1|product_2|date_product_2|..
1234 |3011| X1 | Y2 |..| | |YES |30/11/2019 |..
In the original files I have around 600k rows and more than 300 columns (meaning around 100 different products) in table 1 and around 800 possible combinations of segments in table 2.
The algorithm I need to implement (very generally):
for x=1 to 100
IF product_x = YES THEN date_product_x = date + "Seach for result in table2"
Is there a reasonable way how to change the "date_product_x" columns based on the 2nd table or would it be better to find a different solution?
Thanks a lot!
I can only give you a general approach, because the information in your question is general (for example, why does "end the product on anniversary" translate to "30/11/2019"? It's not explained in the question, so I assume you're going to be able to handle that part of the logic).
You can approach this by using an UNPIVOT on Table 1 to get a structure like:
cont_n | segment1 | segment2 | product_number | product_date
You will UNPIVOT..FOR date_product_1 thru date_product_100. You'll either have to type out all 100 column names, or use dynamic sql to build the whole thing.
You'll do some string manipulation to grab the "x" portion of "date_product_x", and turn it into "prod_x", and then you can join to the second table on the two segment columns and the "prod_x" column, get the result column value, and do whatever rules you're doing to get the value you want for date_product_x.
Finally, you take that result, and PIVOT it back to the one-row-per-contract form, and JOIN it to your original table to UPDATE the date_product_x columns.

Is it possible to match the "next" unmatched record in a SQL query where there is no strictly unique common field between tables?

Using Access 2010 and its version of SQL, I am trying to find a way to relate two tables in a query where I do not have strict, unique values in each table, using concatenated fields that are mostly unique, then matching each unmatched next record (measured by a date field or the record id) in each table.
My business receives checks that we do not cash ourselves, but rather forward to a client for processing. I am trying to build a query that will match the checks that we forward to the client with a static report that we receive from the client indicating when checks were cashed. I have no control over what the client reports back to us.
When we receive a check, we record the name of the payor, the date that we received the check, the client's account number, the amount of the check, and some other details in a table called "Checks". We add a matching field which comes as close as we can get to a unique identifier to match against the client reports (more on that in a minute).
Checks:
ID Name Acct Amt Our_Date Match
__ ____ ____ ____ _____ ______
1 Dave 1001 10.51 2/14/14 1001*10.51
2 Joe 1002 12.14 2/28/14 1002*12.14
3 Sam 1003 50.00 3/01/14 1003*50.00
4 Sam 1003 50.00 4/01/14 1003*50.00
5 Sam 1003 50.00 5/01/14 1003*50.00
The client does not report back to us the date that WE received the check, the check number, or anything else useful for making unique matches. They report the name, account number, amount, and the date of deposit. The client's report comes weekly. We take that weekly report and append the records to make a second table out of it.
Return:
ID Name Acct Amt Their_Date Unique1
__ ____ ____ ____ _____ ______
355 Dave 1001 10.51 3/25/14 1001*10.51
378 Joe 1002 12.14 4/04/14 1002*12.14
433 Sam 1003 50.00 3/08/14 1003*50.00
599 Sam 1003 50.00 5/11/14 1003*50.00
Instead of giving us back the date we received the check, we get back the date that they processed it. There is no way to make a rule to compare the two dates, because the deposit dates vary wildly. So the closest thing I can get for a unique identifier is a concatenated field of the account number and the amount.
I am trying to match the records on these two tables so that I know when the checks we forward get deposited. If I do a simple join using the two concatenated fields, it works most of the time, but we run into a problem with payors like Sam, above, who is making regular monthly payments of the same amount. In a simple join, if one of Sam's payments appears in the Return table, it matches to all of the records in the Checks table.
To limit that behavior and match the first Sam entry on the Return table to the first Sam entry on the Checks table, I wrote the following query:
SELECT return.*, checks.*
FROM return, checks
WHERE (( ( checks.id ) = (SELECT TOP 1 id
FROM checks
WHERE match = return.unique1
ORDER BY [our_date]) ));
This works when there is only one of Sam's records in the Return table. The problem comes when the second entry for Sam hits the Return table (Return.ID 599) as the client's weekly reports are added to the table. When that happens, the query appropriately (for my purposes) only lists that two of Sam's checks have been processed, but uses the "Top 1 ID" record to supply the row's details from the Return table:
Checks_Return_query:
Checks.ID Name Acct Amt Our_Date Their_Date Return.ID
__ ____ ____ ____ _____ ______ ________
1 Dave 1001 10.51 2/14/14 3/25/14 355
2 Joe 1002 12.14 2/28/14 4/04/14 378
3 Sam 1003 50.00 3/01/14 3/08/14 433
4 Sam 1003 50.00 4/01/14 3/08/14 433
In other words, the query repeats the Return table info for record Return.ID 433 instead of matching Return.ID 599, which is I guess what I should expect from the TOP 1 operator.
So I am trying to figure out how I can get the query to take the two concatenated fields in Checks and Return, compare them to find matching sets, then select the next unmatched record in Checks (with "next" being measured either by the ID or Our_Date) with the next unmatched record in Return (again, with "next" being measured either by the ID or Their_Date).
I spent many hours in a dark room turning the query into various joins, and back again, looking at functions like WHERE NOT IN, WHERE NOT EXISTS, FIRST() NEXT() MIN() MAX(). I am afraid I am way over my head.
I am beginning to think that I may have a structural problem, and may need to write the "matched" records in this query to another table of completed transactions, so that I can differentiate between "matched" and "unmatched" records better. But that still wouldn't help me if two of Sam's transactions are on the same weekly report I get from my client.
Are there any suggestions as to query functions I should look into for further research, or confirmation that I am barking up the wrong tree?
Thanks in advance.
I'd say that you really need another table of completed transactions, it could be temporary table.
Regarding your fears "... if two of Sam's transactions are on the same weekly report ", you can use cursor in order to write records "one-by-one" instead of set based transaction.

How to structure table for Distributed Change Control of a Row

If I have a table named Person(ID, Name, Version), I want to make it so that users entering updates to existing person creates a newly versioned row. Such that a person may take and make changes. Exactly the same way Hg et al does versioning. The idea is that if a person A enters x, person B can edit it and have a local copy of x.1, With all the beauty of merging back into x.
Would it be best to structure it like:
Person(ID,Name,Version), and use some kind of Dewey system for revisions
or
Person(ID,Name,Version, Creator), and use a Version local to the user
or some kind of
Person(ID,Name,Person_Id, Creator),
Or are there better idea's for row centric versioning?
The end result in the UI would look like:
You have x in your list
person A has updated x [ignore],[update with A's changes]
person B has updated x [ignore],[update with B's changes]
You could at a start and end date to the row:
PersonID StartDt EndDt UpdatedBy Other columns
1 2012-01-01 2012-02-01 Fred
1 2012-02-01 2012-03-01 Alice
1 2012-03-01 Thomas
Each edit is logged with the person that made the change. You can also see during which period the row was active. To search for the current row, you can use the condition that EndDt is null.

Table row as column in Access form

I am trying to create a form in Access based on a table and I would like to use the table's rowdata as the column title in the form.
What I mean is, I have a table as follows which shows the effort booked by employees towards specific projects on a monthly basis:
ProjectNo EmployeeName HoursBooked Month/Year
23 Mark 68 01/01/2012
23 Mark 55 01/02/2012
54 John 22 01/02/2012
23 John 52 01/02/2012
...etc
and from this I would like to create a singular form where the data is displayed as follows for each project. For example, for project 23:
Month/Year Mark John
01/01/2012 68 0
01/02/2012 55 52
and someone should be able to go to this form, edit the numbers and the table should reflect these changes.
Could someone guide me on how to achieve this? I have been trying to achieve this in Access but unfortunately my Forms knowledge is not that great.
Thanks!
Let say that 1st form is having name: FrmProject & project id is displayed in ProjectID control.
The second form is having name FrmProjectDetails. Considering that project data is stored in table TblPorjects.
On the Click event of ProjectID control in FrmProject you write the code as follow:
Private Sub ProjectID_Click()
docmd.open "FrmProjectDetails"
Form_FrmProjectDetails.RecordSource= "select * from TblProjects where ProjectID=" & Me.ProjectID
End Sub
Use the wizard to create a crosstab query. It will give you what you want. You can set the Source Object of Subform Control to Query.NameOfCrosstabQuery.
However, you will not be able to edit form data based on a crosstab.
It is possible that someone will see an alternative solution if you say why you wish to present the data in this way.
EDIT some details on a possible approach: http://wiki.lessthandot.com/index.php/Crosstabs%2C_forms_and_updating

Crystal reports - missing fields

using Crystal reports 10 linked to an excel document. Would like to pull the dinner field but also pull country and Company name from row that dont have it, this are linked via Bookingref. Example below. I've tried sub-reports and supressing unwanted fields but can't get it right. Also I can't make changes in excel doc as it's 1000+ records, which is exported from an online system weekly.
Id BookingRef Country CompanyName Surname Forname Dinner
1 001 UK Company1 John Andrews
2 001 Mary Jane 1
3 001 Tom Andrews 1
4 002 Germany Company2 Lee Jones
5 003 Germany Company3 Peter Lee 1
6 003 Sofie Lee 1
OK I am not sure I understand the full extent of your problem but let's start with the Country and Company name and see if I can get you moving forward. Instead of putting the Country field directly on the report you could use a formula field and do something like this:
IF {#BookingRef} = "001" Then
"UK"
Else IF {#BookingRef} = "002" Then
"Germany"
Else
"Unnamed"
Now you just put the formula field where the country field used to be and it will put the right country in bases on the BookingRef code. This, however, is only practical if you are working with a small number of Country / Company Names or possibly a big list that never changes although I would caution against the latter.
The other thing you could do is create a table in any database that holds the BookingRef, Company and Country values, link the BookingRef fields from both "databases" and then just drop the fields on your report.
If I am missing the point of your question please be real specific about what it is you are trying to accomplish and what is and is not working in your current solution.