how to compare to similar rows in two identical sql tables - sql

I have two identical tables as following:
Table 1
Student#|name|Course1#|Course2#|Course3#
456 abc 12 76 89
789 def 09 13 76
345 ghi 56 34 14
Table 2
Student#|name|Course1#|Course2#|Course3#
456 abc 12 76 89
789 def 90 13 76
345 ghi 56 34 14
Table1 will contain latest data and table 2 will keep a copy of table 1. Table 2 is updated everytime after updation of table 1 and I do not want a complete truncation and insertion. I want to fire a query which will compare these two tables and return only those rows in which value is changed. On the basis of these vale i can fire an update in table 2.
For eg: in table 1, student# 789 have a value changes for course 1# as 90 from 09, but table 2 still have old value. When I fire query i should get result like:
Student#|name|Course1#|Course2#|Course3#
789 def 90 13 76

It would rarely make sense to have two copies of the same data let alone try to keep two copies of data and periodically try to keep them in sync. So the premise seems rather suspect.
It sounds like you are looking for something like
UPDATE table2 t2
SET (course1, course2, course3) = (SELECT course1, course2, course3
FROM table1 t1
WHERE t1.student = t2.student)
WHERE EXISTS( SELECT 1
FROM table1 t1
WHERE t1.student = t2.student
AND ( t1.course1 != t2.course1
OR t1.course2 != t2.course2
OR t1.course3 != t2.course3) );
This won't account for cases where either table has a NULL value. If you want to replace a NULL value in table2 with a non-NULL value from table1 if it is available, and assuming -1 is not a valid value for that column, the predicate in the EXISTS clause would change to look something like t1.course1 != nvl(t2.course1, -1).

Create trigger on T1 for INSERT,DELETE,UPDATE ,in the trigger put the dirty KEYS/rows in onather table, then periodly check the dirty tracing table. Or in the trigger update T2 directlly.

Related

How to UPDATE single column with returned data in SQL?

Given the output of a SELECT statement below, how would I update all records' "CameraLocationID" field to the value in the "LocationID" field?
Current State:
ItemID CameraID CameraLocationID LocationID
2 23038 NULL 335
3 23039 NULL 67
4 23040 NULL 34
5 23041 NULL 234
Desired Output:
ItemID CameraID CameraLocationID LocationID
2 23038 335 335
3 23039 67 67
4 23040 34 34
5 23041 234 234
What I've tried:
Selecting, and then trying to copy/paste single column using SSMS
An INSERT INTO (knowing pretty much this wouldn't work as I'm not really "INSERTING" per se
Copy and pasting the LocationID values into Excel, and then trying to c/p back into SQL using SSMS
Is there a SQL way of doing this fairly quickly?
EDIT: For future travelers, CameraLocationID and LocationID are in different tables. The SELECT brings them together into a single output before applying the UPDATE.
Update myTable set CameraLocationID = LocationID;
is all you need.
If locationID is coming from a different table you need an ID field there for linking, and I think the source and target both have CameraId field:
Update targetTable
set CameraLocationId = sourceTable.LocationId
from sourceTable
where targetTable.CameraID = sourceTable.CameraID;
If that is not the case, please supply full information on table structures and current data.

Copying new rows and certain changes from one SQL DB table to another

I have two databases on different servers. I've created a link between the two and am wanting to insert new data and specific changed data on timed intervals, one-way, with a few caveats (If it was just a straight, one-time copy, I'd have no issue). For example:
Table 1 columns to be copied: ID, H, W, D, Name, WO, RM, QTY, PN.
Table 2 will have the same columns, plus: Stage, SD.
I need to, on timed intervals, copy the data from Table1 to Table2. The ID is unique and it needs to check if any other of the originally copied data changed, and if it did, replace it with the new data as well as add new rows. What it cannot do it replace data in the additional columns in Table2, even if some of the original data has changed.
UPDATE (EXAMPLE)
Table1
ID H W D Name WO RM QTY PN
40101 32 32 12 Prod1 9501 Front 1 23.1
40102 12 12 6 Prod2 9501 Back 19 29.5
40103 18 36 12 Prod3 9502 Side 5 43.3
Table2
ID H W D Name WO RM QTY PN Stage SD
40101 32 32 12 Prod1 9501 Front 1 23.1 2018-02-15 2018-02-22
40102 12 12 6 Prod2 9501 Back 19 29.5 2018-02-16 2018-02-22
40103 18 36 12 Prod3 9502 Side 5 43.3 2018-02-16 2018-02-22
As shown above, Table1 data is copied to Table2 through SQL insert query; there are more columns in Table1 than what is shown, but those are the only columns included. There will be times that the data changes for a row in Table1, possibly just one column or maybe all of them except ID. When copying the data to Table2, it should insert new records, update the columns that have changed (only ones copied from Table1) in existing rows and not others columns in Table2 that may or may not already have data in them, such as the Stage or SD column in Table2.
Using Merge Procedure is best approach in this case. This works, but also depends on any constraints on source and destination tables. Merge proc is always optimized if your tables are aligned properly with data types , constraints.
CREATE PROCEDURE TableInsert_Update
AS
BEGIN
MERGE Table2 AS TARGET
USING Table1 AS SOURCE
ON (TARGET.id = SOURCE.id)
WHEN MATCHED
THEN
UPDATE
SET TARGET.col1 = SOURCE.col1
,TARGET.col2 = SOURCE.col2
,TARGET.col3 = SOURCE.col3
,TARGET.col4 = SOURCE.col4
--add more columns
WHEN NOT MATCHED BY TARGET
THEN
INSERT (
col1
,col2
,col3
,col4
)
--add more columns
VALUES (
SOURCE.col1
,SOURCE.col2
,SOURCE.col3
,SOURCE.col4
--add more columns
);
END

How to update a target table with Merge statement

I'm trying to update a column in target table using merge statement by joining target table with source tables and getting the following error.
I need to compare a offer_id, order_Date and Doc_receipt_date from TRADE table with offer_trade_by_date, offer_start_date, offer_end_date. Here I'm trying to verify the trade is done with in the time period. If TRADE is done in time then it's pass the check (i.e 'Y'). If TRADE is not done in time then it didn't pass the check (i.e 'N'). If we don't have any information to check the condition (i.e when DOCK_RECEIPT_DATE is NULL) then ('X'). To achieve this check I wrote below code and getting the following error.
ORA: 30926 unable to get a stable set of rows in the source table.
Check the data in my tables below.
TRADE / Target table
KEYID DPBL_OFFER ORD_DATE DOC_RECPT_DT TRADE_DATE_MET
1 107 30-SEP-17 01-JAN-17 X
2 107 22-SEP-17 NULL X
3 107 07-OCT-17 NULL X
4 107 24-NOV-17 28-NOV-17 X
5 106 24-AUG-17 11-SEP-17 X
6 105 11-JUN-17 NULL X
7 108 05-SEP-17 13-SEP-17 X
8 109 28-JUL-17 10-AUG-17 X
9 110 01-SEP-17 14-SEP-17 X
PROD_OFFER /Source table)
Offer_id Trade_by_Date
106 14-OCT-17
107 14-NOV-17
105 02-AUG-17
108 18-NOV-17
109 14-OCT-17
110 18-NOV-17
OFFER_START_END_V /Source Table 2)
Offer_id Offer_Period Offer_Start_Date Offer_End_Date
106 1 27-JUL-17 27-JUL-17
106 2 28-JUL-17 14-OCT-17
107 1 15-SEP-17 23-JAN-18
105 1 01-JUN-17 02-AUG-17
108 1 23-AUG-17 14-SEP-17
108 2 16-SEP-17 19-SEP-17
110 1 23-AUG-17 14-SEP-17
110 2 16-SEP-17 19-SEP-17
109 1 02-JUL-17 12-NOV-17
Here keyid in my target table is PK and DPBL_OFFER id is offer_id from target table and isn't FK.
Check below code
MERGE INTO TRADE TB
USING (
SELECT T1.KEYID, T1.DPBL_OFFER
, CASE WHEN T1.ORD_DATE >= T3.OFFER_START_DATE AND
T1.ORD_DATE <= T2.TRADE_BY_DATE AND
T1.COD_RECPT_DATE <= T3.OFFER_END_DATE
THEN 'Y'
WHEN T1.ORD_DATE < T3.OFFER_START_DATE AND
T1.ORD_DATE > T2.TRADE_BY_DATE AND
T1.COD_RECPT_DATE > T3.OFFER_END_DATE
THEN 'N'
ELSE 'X'
END AS TRADE_DATE_MET
FROM TRADE T1
JOIN PROD_OFFER T2
ON T1.DPBL_OFFER_ID = T2.OFFER_ID
JOIN OFFER_START_END_V T3
ON T1.DPBL_OFFER_ID = T3.OFFER_ID) JT
ON TB.KEYID = JT.KEYID
AND TB.DPBL_OFFER_ID = JT.OFFER_ID
WHEN MATCH THEN
UPDATE SET TB. TRADE_DATE_MET = JT.TRADE_DATE_MET;
Can some one help me to overcome this error.?
FYI:- I'm using Oracle 11g.
That error generally means that there is at least one row in the target table (at least one, there may be many) for which there are at least TWO different rows in the source table (or result of three-table join, in your case) satisfying the ON condition(s) in the MERGE statement - and for which the values used in the UPDATE clause are actually different.
In your case: for KEYID = 5, DPBL_OFFER is 106. This joins to one row in PROD_OFFER and two different rows in OFFER_START_END_V. And the TRADE_END_MET is different for the two resulting rows in the three-table join. (Or, if it is the same - let's say 'N' for both - for this KEYID, then perhaps for KEYID= 7, with DPBL_OFFER = 108, which also joins to two different rows in the last table, the resulting TRADE_END_MET is not the same in both rows.)
This kind of error is usually fatal, since it is in fact an error in logic, regardless of code (or even of language). That is, even if you express the problem in common language and you try to solve it with paper and pencil, you can't, because it is self contradictory.
It is similar to, but more complicated than: Target table has columns ID (primary key) and FLAG (currently null). Source has columns ID and flag. Target has one row (1, null). Source has two rows, (1, 'Y') and (1, 'N'). You want to use the source to update the flag in the target. Do you see why that doesn't make sense? This is exactly the kind of problem you are having.
Run the three-way join by itself (the "source table" for the MERGE) and inspect the TRADE_END_MET values for KEYID = 5 and 7 - you will likely find the problem.

Sql: have a column return 1 or 0 denoting if an id exists in a table for a predetermined groupID

I wrote the following SQL to create a column that I can use to populate check boxes in a Grid to manage user permissions.
SELECT access_b2b.access_id,
access_b2b.description,
'active'= CASE
WHEN access_group.group_id IS NOT NULL THEN 1
ELSE 0
END
FROM access_b2b
LEFT JOIN access_group
ON access_group.access_id = access_b2b.access_id
WHERE ( access_group.group_id = 10
OR access_group.group_id IS NULL )
However, it does not select all of the entries from access_b2b. The issues is with the last line:
where (access_group.group_id=10 or access_group.group_id is null)
Without it, i get duplicate entries returned with different active values. Also, I realized that this is not the proper condition, because an entry in access_group might exist for a different access_group.group_id, meaning that not all the remaining entries will be pulled in with the access_group.group_id is null.
I am trying to write my condition so that if does something along the lines of:
This is the format I was trying to follow:
Where For Each unique access_id in access_group
select the one where group_id=10
if no group_id=10
select any other one
end
end
Ultimately, the goal is to have a column returned with 1 or 0 denoting if the access_id exists for a predetermined group id.
Please note that throughout this explanation I used group_id=10 for simplification, it will be later replaced with a SqlParameter.
Any help is appreciated, thank you so much!
SAMPLE DATA (only useful columns shown to simplify data)
access_group
access_id group_id
27 1
27 11
28 1
28 11
33 1
33 3
33 11
43 11
44 1
44 10
44 11
...
access_b2b
access_id description
1 Add
2 Edit
3 Delete
4 List
5 Payments
6 Open Files
7 Order
8 Mod
...
Change the query to and it should work:
SELECT access_b2b.access_id,
access_b2b.description,
'active'= CASE
WHEN access_group.group_id IS NOT NULL THEN 1
ELSE 0
END
FROM access_b2b
LEFT JOIN access_group
ON access_group.access_id = access_b2b.access_id
AND ( access_group.group_id = 10
OR access_group.group_id IS NULL )
If you don't want the records to be filtered by the WHERE clause, move the condition in the JOIN.
The JOIN will keep the lines and populate them with NULL if the condition is not met, while the WHERE clause will filter the result set.

SQL - Delete value if incremental pattern not met

I have a table with a column of values with the following sample data that has been pulled for 1 user:
ID | Data
5 Record1
12 NULL
13 NULL
15 Record1
20 Record12
28 NULL
31 NULL
35 Record12
37 Record23
42 Record34
51 NULL
53 Record34
58 Record5
61 Record17
63 NULL
69 Record17
What I would like to do is to delete any values in the Data column where the Data value does not have a start and finish record. So in the above Record 23 and Record 5 would be deleted.
Please note that the Record(n) may appear more than once so it's not as straight forward as doing a count on the Data value. It needs to be incremental, a record should always start and finish before another one starts, if it starts and doesnt finish then I want to remove it.
Sadly SQL Server 2008 does not have LAG or LEAD which would make the operation simpler.
You could use a common table expression for finding the non consecutive (non null) values, and delete them;
WITH cte AS (
SELECT *, ROW_NUMBER() OVER (ORDER BY id) rn FROM table1 WHERE data IS NOT NULL
)
DELETE c1 FROM cte c1
LEFT JOIN cte c2 ON (c1.rn = c2.rn+1 OR c1.rn = c2.rn-1) AND c1.data = c2.data
WHERE c2.id IS NULL
An SQLfiddle to test with.
If you just want to see which rows would be deleted, replace DELETE c1 with SELECT c1.*.
...and as always, remember to back up before running potentially destructive SQL for random people on the Internet.