Synchronize a set of table records via triggers - tsql

I have a table from an existing product (so no schema changes are possible) that have a schema similar to the following:
objectId, typeId, value
What I need to do is to essentially make several typeIds linked and in synch with each other. See the example below. There will be around 35 sets of 3-4 linked typeIds each.
I cannot figure out reasonable way to do this that is performant, handles multi-row inserts and prevents trigger recursion. Any suggestions?
Example
typeId 1, 2 and 3 should be linked.
INSERT INTO foo (objectId, typeId, value) VALUES (1, 1, 'bar')
Should result in the table containing the following
╔══════════╦════════╦═══════╗
║ objectId ║ typeId ║ value ║
╠══════════╬════════╬═══════╣
║ 1 ║ 1 ║ bar ║
║ 1 ║ 2 ║ bar ║
║ 1 ║ 3 ║ bar ║
╚══════════╩════════╩═══════╝
Any updates to the value of either of these records should result in the value of all of them to be changed.

In the end, I solved my problem by modifying the "insert" and "update" stored procedures used by the application in question, instead of solving it with triggers.
The insert procedure ended up something like this:
IF #typeId IN (1,2,3)
INSERT INTO foo (objectId, typeId, value) VALUES (#objectId, 1, #value), (#objectId, 2, #value), (#objectId, 3, #value)
ELSE IF
...
ELSE
INSERT INTO foo (objectId, typeId, value) VALUES (#objectId, #typeId, #value)
The update procedure ended up something like this:
IF #typeId IN (1,2,3)
UPDATE foo SET value = #value WHERE objectId = #objectId AND #typeId IN (1,2,3)
ELSE IF
...
ELSE
UPDATE foo SET value = #value WHERE rowId = #rowId

Related

Postgres array comparison - find missing elements

I have the table below.
╔════════════════════╦════════════════════╦═════════════╗
║id ║arr1 ║arr2 ║
╠════════════════════╬════════════════════╬═════════════╣
║1 ║{1,2,3,4} ║{2,1,7} ║
║2 ║{0} ║{3,4,5} ║
╚════════════════════╩════════════════════╩═════════════╝
I want to find out the elements which are in arr1 and not in arr2.
Expected output
╔════════════════════╦════════════════════╗
║id ║diff ║
╠════════════════════╬════════════════════╣
║1 ║{3,4} ║
║2 ║{0} ║
╚════════════════════╩════════════════════╝
If I have 2 individual arrays, I can do as follows:
select array_agg(elements)
from (
select unnest(array[0])
except
select unnest(array[3,4,5])
) t (elements)
But I am unable to integrate this code to work by selecting from my table.
Any help would be highly appreciated. Thank you!!
I would write a function for this:
create function array_diff(p_one int[], p_other int[])
returns int[]
as
$$
select array_agg(item)
from (
select *
from unnest(p_one) item
except
select *
from unnest(p_other)
) t
$$
language sql
stable;
Then you can use it like this:
select id, array_diff(arr1, arr2)
from the_table
A much faster alternative is to install the intarray module and use
select id, arr1 - arr2
from the_table
You should use except for each id and after that group by for each group
Demo
with diff_data as (
select id, unnest(arr1) as data
from test_table
except
select id, unnest(arr2) as data
from test_table
)
select id, array_agg(data order by data) as diff
from diff_data
group by id

PostgreSQL - How to add a new column with a default conditioning on another column?

I have a table like this
Table 1:
Code
1
2
3
4
1
2
I want to add a new column conditioning on the column code.
1 -> A
2 -> B
3 -> C
4 -> D
My expected result:
Table 1:
Code Name
1 A
2 B
3 C
4 D
1 A
2 B
I expecting a code like this:
alter table table_1
add column Name varchar(64) set default case when Code = 1 then "A"
Code = 2 then "B"
Code = 3 then "C"
. . .
,
One straightforward way would be to just maintain a table of codes, and then join to it:
CREATE TABLE codes (Code integer, Name varchar(5));
INSERT INTO codes (Code, Name)
VALUES
(1, 'A'),
(2, 'B'),
(3, 'C'),
(4, 'D');
SELECT t1.Code, t2.Name
FROM table_1 t1
INNER JOIN codes t2
ON t1.Code = t2.Code;
Note that I vote against doing this update, because as your underlying code values change, you might be forced to do the update again. The above approach doesn't have that problem, and you get the correct name when you select.

Trying to get start and end time in hours from a set of 30 minute time intervals and some results aren't returning correctly

I'm trying to get a report of the hours worked out of a Postgresql database. I'm going to use Python and Pandas to format run additional calculations before outputting to reports and I'm using the pd.read_sqq_query() method to pull the data into python using raw SQL.
The information is over multiple tables users, intervals, claimed. Claimed is a many to many mapping to intervals and to users. I'm expecting to get multiple users back so I'm using the PARTITION BY username clause to group them. Please let me know if the layout might be causing the problem as my example below has been simplified some.
I've recently discovered various resources talking about gaps and islands problems and found one that seems to fit my use case that I've adapted to work; Ref: Gaps and islands. It appears to be MSSQL though I don't believe it's mentioned in there.
The problem is that some of the results aren't returning what I expect. I've created a SQL Fiddle with a minimum viable sqlfiddle
This is one of the segments found by the islands. I'm taking the MAX(endtime) and the MIN(starttime) but in some cases I am missing the final interval.
Ex: The following table has one segment and I would expect it to show starttime as 2020-03-08T0:00:00 and endtime as 2020-03-08T4:00:00 but I'm actually getting the endtime as 2020-03-08T3:30:00
╔═════════════╦═════════════════════╦═════════════════════╗
║ Username ║ Start Time ║ End Time ║
╠═════════════╬═════════════════════╬═════════════════════╣
║ Test User 1 ║ 2020-03-08T02:00:00 ║ 2020-03-08T02:30:00 ║
║ Test User 1 ║ 2020-03-08T02:30:00 ║ 2020-03-08T03:00:00 ║
║ Test User 1 ║ 2020-03-08T03:00:00 ║ 2020-03-08T03:30:00 ║
║ Test User 1 ║ 2020-03-08T03:30:00 ║ 2020-03-08T04:00:00 ║
╚═════════════╩═════════════════════╩═════════════════════╝
This is what I have in the SQLFiddle for the example and there's more data but all for one user.
SELECT username,
islandId,
MIN(starttime) as IslandStartDate,
MAX(endtime) as IslandEndDate
FROM
(SELECT *,
CASE
WHEN Groups.PreviousEndDate >= starttime THEN 0
ELSE 1
END as IslandStartInd,
SUM(CASE
WHEN Groups.PreviousEndDate >= starttime then 0
else 1
end) OVER (PARTITION BY Groups.username
ORDER BY Groups.RN) as IslandId
FROM
( SELECT ROW_NUMBER() over (PARTITION BY tr.username
order by tr.starttime,
tr.endtime) as rn ,
tr.username ,
tr.starttime ,
tr.endtime ,
LAG(tr.endtime, 1) OVER (PARTITION BY tr.username
ORDER BY tr.starttime,
tr.endtime) as PreviousEndDate
FROM timerange tr
WHERE tr.starttime BETWEEN '2020-03-01' AND '2020-03-20'
ORDER BY tr.username) Groups ) Islands
Group BY username,
islandid
ORDER BY username,
IslandStartDate
I have restructured the gaps-and-islands approach using window functions and common table expressions to make it easier to follow.
You can uncomment the commented queries at the bottom (one at a time) to see how the strategy works step by step.
The sqlfiddle.
with gaps as (
select *,
case
when starttime = lag(endtime) over (partition by username
order by starttime) then 0
else 1
end as gap_begin_row_marker
from timerange
), grp_numbers as (
select username, starttime, endtime,
sum(gap_begin_row_marker) over (partition by username
order by starttime) as grp_num
from gaps
), collapsed_intervals as(
select grp_num, username, min(starttime) as starttime, max(endtime) as endtime
from grp_numbers
group by grp_num, username
), summed_time as (
select username, sum(endtime - starttime) as time_claimed
from collapsed_intervals
group by username
)
/* select * from gaps; */
/* select * from grp_numbers; */
/* select * from collapsed_intervals; */
select * from summed_time;

TSQL Reverse LIKE Statement

To give you context here is my db schema.
Fruit Table
Id Name
1 Gala Apples
2 Navel Oranges
3 Peach
4 Mandarin Oranges
5 Kiwi
6 Fuji Apples
Intersect Table:
FruitId CrossRefFruitId
1 6
2 4
So my intersect table has the fruit values.
1 (Gala Apples) are related to 6 (Fuji Apples)
2 (Navel Oranges) are related to 4 (Mandarin Oranges)
In the UI say the user is viewing details about Gala Apples. I want to be able to have a "See Also: Fuji Apples".
Now I'm told that I do NOT want to have reverse entries so 1,6 is good but it is a waste to have 6,1 which makes sense.
So I'm trying to write a TSQL check that will identify if 1,6 or 6,1 exists.
Ultimately this would go into an INSERT trigger that would check the incoming row to see if the combination exists then allow insert or skip.
Googling I find REVERSE which is for reversing a string and end up with a lot of hits for EXCEPT and INTERSECT.
So even just knowing the proper terminology to google so I can self educate would be appreciated.
create table #test
(
id1 int,
id2 int
)
insert into #test
select 1,6
union all
select 2,5
union all
select 6,1
union all
select 5,3
union all
select 5,2
select * from #test t1
where exists(
select 1 from #test t2 where t1.id1=t2.id2 and t1.id2=t2.id1)
Updating as per question:
Your trigger should contain below line of code which checks existence of rows which are reversed..
if exists(
select 1 from
#test t1
join
inserted i
on i.id1=t1.id2 and i.id2=t1.id1
)
begin
rollback tran
--some message---
end

Valid data in A group- SQL Grouping

May some one please help me how to achieve the below scenario.
two tables one called as driver and one called as electronic.
-- Driver table
DECLARE #DRIVER TABLE
(
Parenttype varchar (50),
childtype varchar (50)
)
INSERT #DRIVER
SELECT 'Carbon Composite Resistor','Ceramic %'
-- Electronic Table
DECLARE #ELECTRONIC TABLE
(
PARENTSKU varchar (50),
ROLLOVER varchar (50),
CHILDSKU varchar (50),
TYPE varchar (50)
)
INSERT #ELECTRONIC
SELECT 'BIN19-1405','LEAD','19-1405','Carbon Composite Resistor' UNION ALL
SELECT 'SAM92-140','MERCURY','92-140','Carbon Composite Resistor' UNION ALL
SELECT 'SAB45-155','LEAD','45-155','Carbon Composite Resistor' UNION ALL
SELECT 'NIP69-153','SULPHUR','69-153','Carbon Composite Resistor' UNION ALL
SELECT 'DIP19-1508','LEAD','19-1508','Carbon Composite Resistor' UNION ALL
SELECT 'ZQC140012','ROHS','140012','Carbon Composite Resistor' UNION ALL
SELECT 'LHH543012','ROHS','543012','Carbon Composite Resistor' UNION ALL
SELECT 'JWC592013','ROHS','592013','Carbon Composite Resistor' UNION ALL
SELECT 'GHY846013','ROHS','846013','Carbon Composite Resistor' UNION ALL
SELECT 'ZQC140012','ROHS','140012','Ceramic capacitors LARGE' UNION ALL
SELECT 'LHH543012','ROHS','543012','Ceramic capacitors SMALL' UNION ALL
SELECT 'JWC592013','ROHS','592013','Ceramic capacitors MEDIUM' UNION ALL
SELECT 'GHY846013','ROHS','846013','Ceramic capacitors' UNION ALL
SELECT 'MCN8LTC8K','ROHS','8LTC8K','Double-layer capacitors' UNION ALL
SELECT 'PRM81150','ROHS','81150','Tantalum capacitors' UNION ALL
SELECT 'PRM846013','ROHS','846013','Hybrid capacitors '
Here I am looking for output which meet two below condition
1st: ALL those Parent SKU which contain type which is equal to the Parenttype available in Driver table and rollover other than ROHS
2ND: ALL THOSE Parent SKU Records whose rollover are ROHS but only present with parent type but not with Child Type
Expected Output
╔════════════╦══════════╦══════════╦═══════════════════════════╗
║ PARENTSKU ║ ROLLOVER ║ CHILDSKU ║ TYPE ║
╠════════════╬══════════╬══════════╬═══════════════════════════╣
║ BIN19-1405 ║ LEAD ║ 19-1405 ║ Carbon Composite Resistor ║
║ SAM92-140 ║ MERCURY ║ 92-140 ║ Carbon Composite Resistor ║
║ SAB45-155 ║ LEAD ║ 45-155 ║ Carbon Composite Resistor ║
║ NIP69-153 ║ SULPHUR ║ 69-153 ║ Carbon Composite Resistor ║
║ DIP19-1508 ║ LEAD ║ 19-1508 ║ Carbon Composite Resistor ║
║ MCN8LTC8K ║ ROHS ║ 8LTC8K ║ Double-layer capacitors ║
║ PRM81150 ║ ROHS ║ 81150 ║ Tantalum capacitors ║
║ PRM846013 ║ ROHS ║ 846013 ║ Hybrid capacitors  ║
╚════════════╩══════════╩══════════╩═══════════════════════════╝
Thanks a lot.
try this
SELECT E.PARENTSKU ,
E.ROLLOVER ,
E.CHILDSKU ,
E.TYPE
FROM #ELECTRONIC AS E
WHERE ( E.Type IN ( SELECT D.Parenttype
FROM #DRIVER AS D )
AND E.ROLLOVER != 'ROHS'
)
OR NOT EXISTS ( SELECT NULL
FROM #DRIVER AS D
WHERE ( D.Parenttype = E.TYPE )
OR ( E.TYPE LIKE D.childtype ) )
output