Get the row with latest start date from multiple tables using sub select - sql

I have data from 3 tables as copied below . I am not using joins to get data. I dont know how to use joins for multiple tables scenario. My situation is to update the OLD(eff_start_ts) date rows to sydate in one of the tables when we find the rows returned for a particular user is more than 2. enter code here
subscription_id |Client_id
----------------------------
20685413 |37455837
reward_account_id|subscription_id |CURRENCY_BAL_AMT |CREATE_TS |
----------------------------------------------------------------------
439111697 | 20685413 | -40 |1-09-10 |
REWARD_ACCT_DETAIL_ID|REWARD_ACCOUNT_ID |EFF_START_TS |EFF_STOP_TS |
----------------------------------------------------------------------
230900968 | 439111697 | 14-06-11 | 15-01-19
47193932 | 439111697 | 19-02-14 | 19-12-21
243642632 | 439111697 | 18-03-23 | 99-12-31
247192972 | 439111697 | 17-11-01 | 17-11-01
The SQL should update the EFF_STOP_TS of last table except the second row - 47193932 bcz that has the latest EFF_START_TS.
Expected result is to update the EFF_STOP_TS column of 230900968, 243642632 and 247192972 to sysdate.

As per my understanding, You need to update it per REWARD_ACCOUNT_ID. So, You can try the below code -
UPDATE REWARD_ACCT_DETAIL RAD
SET EFF_STOP_TS = SYSDATE
WHERE EFF_START_TS NOT IN (SELECT MAX(EFF_START_TS)
FROM REWARD_ACCT_DETAIL RAD1
WHERE RAD.REWARD_ACCOUNT_ID = RAD1.REWARD_ACCOUNT_ID)

Related

How can I trigger an update to a value in a table when criteria is met on a different table?

Aware there is an almost identical question here, but that covers the SQL query required, rather than the mechanism of event triggering.
Lets say I have two tables. One table contains performance data for each staff member each week. The other table is a table that holds the staff members information. What I want is to update a value in the table to a Y or N based on whether that staff member left at the week date.
staffTable
+----------+----------------+------------+
| staff_id | staff_name | leave_date |
+----------+----------------+------------+
| 1 | Joseph Blogges | 2020-01-24 |
| 2 | Joe Bloggs | 9999-12-31 |
| 3 | Joey Blogz | 9999-12-31 |
+----------+----------------+------------+
targetTable
+------------+----------+--------+-----------+
| week_start | staff_id | target | left_flag |
+------------+----------+--------+-----------+
| 2020-01-13 | 1 | 10 | N |
| 2020-01-20 | 1 | 10 | N |
| 2020-01-27 | 1 | 8 | Y |
+------------+----------+--------+-----------+
What I am trying to do is have the left_flag automatically change from 'N' to 'Y' when the week_start value is greater than leave_date of the staff member (in the other table).
I have tried successfully putting this into a view, which works, but the problem is that existing applications, views and queries will need to all reference a new view instead of a table and I want to be able to query the data table as my front-end has issues interacting in live with a view instead of a table.
I have also successfully used a UDF to return the leave_date and then create computed column that will check if this UDF variable is greater than the start_date column and this worked fine until I realised that the UDF is the most resource consuming query on the entire server and is completely disproportionate.
Is there a way that I can trigger an update to the staffTable when a criteria is met in another table, or is there a totally better and different way of doing this? If it can't be done easily, I'll try to switch to a view and work around it in the front-end.
I'm going to describe the process rather than writing the code.
What you are describing can be accomplished using triggers on staffTable. When a new row is inserted or updated the trigger would change any rows in targetTable. This would be an after insert/update trigger.
The heart of the trigger would be:
update tt
set left_flag = 'Y'
from targettable tt join
inserted i
on tt.staff_id = i.staff_id
where i.leave_date < tt.week_start and
tt.left_flag <> 'Y';

Update part of the table for a specific category

Let's imagine I have a similar table to this one:
ID | Country | time | location 1 | location 2 | count_clients
------------------------------------------------------------------
1 | PL |2019-01-01 | JAK | ADD3 | 23
2 | PL |2019-03-01 | GGF | ADD5 | 34
3 | PL |2019-01-01 | J3K | 55D3 | 67
4 | NL |2019-04-01 | FDK | AGH3 | 2
5 | NL |2019-01-01 | GGK | AFF3 | 234
It's an aggregated table. Source contains one row per client, in my table it's aggregated showing no. of clients per country, time, location 1 and location 2. It's updated by loading new rows only (new dates). First they are loaded to stage table then, after some modifications, to final table. The values loaded to stage table are already aggregated and stage table contains only new rows.
BUT I just learned that rows in source table can be deleted - it means the "count_clients" value can change or can be deleted. What's also important - I know which COUNTRY, location 1 and location 2 are affected but I don't know WHEN they were changed (was it before or after last load? I don't know).
Do you know any smart ways to handle it? I currently load new rows + rows that were affected by change to stage tables, then remove affected rows from final table and load the stage rows to final table.
The source table is huge. I'm looking for a solution that will allow me to update only part of the table affected by the updates. Please remember that in stage table I have only new rows that needs to be inserted + the rows that was changed. I wanted to use the MERGE statement but to do that I would need to use a part of the table as a target not the whole table. I tried to do it but it didn't work.
I tried to do something like:
MERGE INTO (select country, time, location1, location 2, count from myFinalTable join stage table on country=country and location=location) --target = only rows affected by change
USING myStageTable
ON country = country and location=location
WHEN MATCHED THEN
UPDATE
SET count = count
WHEN NOT MATCHED BY TARGET then INSERT --insert new uploads
WHEN NOT MATCHED BY SOURCE then DELETE
but it looks like I can't use the 'select' statement in target..?

Selecting Sorted Records Prior to Target Record

The background to this question is that we have had to hand-roll replication between a 3rd party Oracle database and our SQL Server database since there are no primary keys defined in the Oracle tables but there are unique indexes.
In most cases the following method works fine: we load the values of the columns in the unique index along with an MD5 hash of all column values from each corresponding table in the Oracle and SQL Server databases and are able to then calculate what records need to be inserted/deleted/updated.
However, in one table the sheer number of rows precludes us from loading all records into memory from the Oracle and SQL Server databases. So we need to do the comparison in blocks.
The method I am considering is: to query the first n records from the Oracle table and then - using the same sort order - to query the SQL Server table for all records up to the last record that was returned from the Oracle database and then compare the two data sets for what needs to be inserted/deleted/updated.
Then once that has been done to load the next n records from the Oracle database and query the records in the SQL Server table that when sorted in the same way fall between (and include) the first and last records in that data set.
My question is: how to achieve this in SQL Server? If I have the values of the nth record (having queried the table in Oracle with a certain sort order) how can I return the range of records up to and including the record with those values from SQL Server?
Example
I have the following table:
| Id | SOU_ORDREF | SOU_LINESEQ | SOU_DATOVER | SOU_TIMEOVER | SOU_SEQ | SOU_DESC |
|-----------------------------------------------------|------------|-------------|-------------------------|------------------|---------|------------------------|
| AQ000001_10_25/07/2004 00:00:00_14_1 | AQ000001 | 10 | 2004-07-2500:00:00.000 | 14 | 1 | Black 2.5mm Cable |
| AQ000004_91_26/07/2004 00:00:00_15.4833333333333_64 | AQ000004 | 91 | 2004-07-26 00:00:00.000 | 15.4333333333333 | 63 | 2.5mm Yellow Cable |
| AQ000005_31_26/07/2004 00:00:00_10.8333333333333_18 | AQ000005 | 31 | 2004-07-26 00:00:00.000 | 10.8333333333333 | 18 | Rotary Cam Switch |
| AQ000012_50_26/07/2004 00:00:00_11.3_17 | AQ000012 | 50 | 2004-07-26 00:00:00.000 | 11.3 | 17 | 3Mtr Heavy Gauge Cable |
The Id field is basically a concatenation of the five fields which make up the unique index on the table i.e. SOU_ORDREF, SOU_LINESEQ, SOU_DATOVER, SOU_TIMEOVER, and SOU_SEQ.
What I would like to do is to be able to query, for example, all the records (when sorted by those columns) up to the record with the Id 'AQ000005_31_26/07/2004 00:00:00_10.8333333333333_18' which would give us the following result (I'll just show the ids):
| Id |
|-----------------------------------------------------|
| AQ000001_10_25/07/2004 00:00:00_14_1 |
| AQ000004_91_26/07/2004 00:00:00_15.4833333333333_64 |
| AQ000005_31_26/07/2004 00:00:00_10.8333333333333_18 |
So, the query has not included the record with Id 'AQ000012_50_26/07/2004 00:00:00_11.3_17' since it comes after 'AQ000005_31_26/07/2004 00:00:00_10.8333333333333_18' when we order by SOU_ORDREF, SOU_LINESEQ, SOU_DATOVER, SOU_TIMEOVER, and SOU_SEQ.

SQL query to find list of primary keys not used

I am trying to make a drop down picker in an Access database to display all the primary keys not used, in this case a date that is limited to the first of the month.
I have 2 tables that are for this use
tblReport
pk date | Data for this record |
05/01/13 | stuff
06/01/13 | stuff
07/01/13 | stuff
08/01/13 | stuff
and
tblFutureDates
pk date | an index
05/01/13 | 1
06/01/13 | 2
07/01/13 | 3
08/01/13 | 4
09/01/13 | 5
10/01/13 | 6
11/01/13 | 7
12/01/13 | 8
I want a query that looks at these two tables and returns the dates that are in the second table that aren't in the first one. I have tried some joins but cannot figure it out. This is what I have thus far:
SELECT tblFutureDates.FutureDate
FROM tblFutureDates RIGHT JOIN tblReport
ON tblFutureDates.FutureDate = tblReport.ReportMonth;
and that returns:
05/01/13
06/01/13
07/01/13
08/01/13
Thanks
This selects dates from tblFutureDates that are NOT IN tblReport
SELECT tblFutureDates.FutureDate
FROM tblFutureDates
WHERE tblFutureDates.FutureDate
NOT IN (SELECT tblReport.ReportMonth FROM tblReport)
You can also use LEFT JOIN WHERE IS NULL and NOT EXISTS for more information about all 3 see this post.

Selecting records in groups by date - possible?

I don't think there is an elegant way to do this, but here goes. Database contains 5000 records with timestamps of 2 years. I need to pull the records under each day of the year.
So it looks like..
09/09/2009 - record938, record2, record493
09/10/2009 - record260, record485, record610
...etc
I cannot use GROUP BY. There are duplicates and that's OK. I need to show them.
Is this possible? PHP/MySQL?
One way of doing it is looping through every day of the year and doing a query with "WHERE DAY(created_at)..." but obviously this isn't elegant.
HOW can I do this? I posted this question before without a satisfactory answer (answer was what I just stated above)
MySQL has the group_concat() aggregate function:
SELECT date(rec_time), group_concat(rec_id)
FROM records GROUP BY date(rec_time);
Will return all rec_id values from table joined by commas, for each date. If you want a separator other than , use group_concat(some_column SEPARATOR '-')
Example
For example if your table looks like:
+--------+---------------------+
| rec_id | rec_time |
+--------+---------------------+
| 1 | 2009-11-28 10:00:00 |
| 2 | 2009-11-28 20:00:00 |
| 3 | 2009-11-27 15:00:00 |
| 4 | 2009-11-27 07:00:00 |
| 5 | 2009-11-28 08:00:00 |
+--------+---------------------+
Then this query gives:
mysql> SELECT date(rec_time), group_concat(rec_id)
-> FROM records GROUP BY date(rec_time);
+----------------+----------------------+
| date(rec_time) | group_concat(rec_id) |
+----------------+----------------------+
| 2009-11-27 | 3,4 |
| 2009-11-28 | 1,2,5 |
+----------------+----------------------+
Caveat
Beware that the result is limited by the group_concat_max_len system variable, which defaults to only 1024 bytes! To avoid hitting this wall, you should execute this before running the query:
SET SESSION group_concat_max_len = 65536;
Or more, depending on how many results you expect. But this value cannot be larger than max_allowed_packet