Deleting rows from a table when i pass a string - sql

Hello everyone i want to create a procedure that receives a int and a string with the ID's when they are like this:
Int CompeteID = 1
String TeamIDs = "1,8,9"
Meaning there are 3 TeamIDs, TeamID = 1, TeamID = 8 and TeamID = 9.
Here is my DBModel: https://i.gyazo.com/7920cca8000436cfe207353aaa7d172f.png
So what i want to do is to Insert on TeamCompete, the SportID and CompeteID when there are no equal SportID and CompeteID.
Like this:
TeamCompeteID TeamID CompeteID
1 1 1
4 8 1
5 9 1
6 8 1 <---- Can't do this
But i also want to delete from TeamCompete the TeamIDs i dont pass onto the procedure for example:
TeamCompeteID TeamID CompeteID
1 1 1
2 3 1 <---- Delete this
3 4 1 <---- Delete this
But I don't want to delete the TeamCompete's that are on the Event table...
Example:
EventID TeamCompeteID
5 3 <---- Can't delete TeamCompeteID 3
-- even though i didn't past it on TeamIDs
I hope you understanded my explanation.

First and foremost, passing data in this manner is very much a bad idea. You should really try to normalise your data and have one value in each field.
If however, you cannot avoid this, I would recommend you use a string splitting function such as Jeff Moden's if you are on a SQL Server version prior to 2016 (the string_split is built into SQL Server 2016) which returns a table you can join to from a given string and delimiter.
Using Jeff's function above:
select *
from dbo.DelimitedSplit8K('1,8,9',',')
Results in:
+------------+------+
| ItemNumber | Item |
+------------+------+
| 1 | 1 |
| 2 | 8 |
| 3 | 9 |
+------------+------+
So to use with your table:
declare #t table(CompeteID int,TeamID nvarchar(10));
insert into #t values(1,'1,8,9');
select t.CompeteID
,s.Item
from #t t
cross apply dbo.DelimitedSplit8K(t.TeamID,',') s
Results in:
+-----------+------+
| CompeteID | Item |
+-----------+------+
| 1 | 1 |
| 1 | 8 |
| 1 | 9 |
+-----------+------+
You can then use this table in the rest of your update/insert/delete logic.

Related

Find records which have multiple occurrences in another table array (postgres)

I have a table which has records in array. Also there is another table which have single string records. I want to get records which have multiple occurrences in another table. Following are tables;
Vehicle
veh_id | vehicle_types
-------+---------------------------------------
1 | {"byd_tang","volt","viper","laferrari"}
2 | {"volt","viper"}
3 | {"byd_tang","sonata","jaguarxf"}
4 | {"swift","teslax","mirai"}
5 | {"volt","viper"}
6 | {"viper","ferrariff","bmwi8","viper"}
7 | {"ferrariff","viper","viper","volt"}
vehicle_names
id | vehicle_name
-----+-----------------------
1 | byd_tang
2 | volt
3 | viper
4 | laferrari
5 | sonata
6 | jaguarxf
7 | swift
8 | teslax
9 | mirai
10 | ferrariff
11 | bmwi8
I have a query which can give output what I expect but its not optimal and may be its expensive query.
This is the query:
select veh_name
from vehicle_names dsb
where (select count(*) from vehicle dsd
where dsb.veh_name = ANY (dsd.veh_types)) > 1
The output should be:
byd_tang
volt
viper
One option would be an aggregation query:
SELECT
vn.id,
vn.veh_name
FROM vehicle_names vn
INNER JOIN vehicle v
ON vn. veh_name = ANY (v.veh_types)
GROUP BY
vn.id,
vn.veh_name
HAVING
COUNT(*) > 1;
This only counts a vehicle name which appears in two or more records in the other table. It would not pick up, for example, a single vehicle record with the same name appearing two or more times.

Find sql connected component between many to many entities

I have two basic entities: financial plan and purchase request. Theese two entities are in many-to-many relationship:
CREATE TABLE FinancialPlan
(
ID int NOT NULL,
PRIMARY KEY (ID)
);
CREATE TABLE PurchaseRequest
(
ID int NOT NULL,
PRIMARY KEY (ID)
);
CREATE TABLE FP_PR
(
FP_ID FOREIGN KEY REFERENCES FinancialPlan(ID),
PR_ID FOREIGN KEY REFERENCES PurchaseRequest(ID)
);
Problem: find all requests, related to specified plan, and all plans, related to requests, related to specified plan, ...
Model could be represented as a graph, where each node represents a plan, or a request, and each edge represents a relationship, then the problem could be rephrased as find connected component, which specified node belongs to.
Example:
Plan Request FP_PR
ID | ID | FP_ID|PR_ID|
----| ----| -----|-----|
1 | 1 | 1 |1 |
2 | 2 | 2 |1 |
3 | 3 | 2 |2 |
4 | 3 |2 |
5 | 4 |2 |
5 |3 |
Find connected component of finplan ID=1
Desired output:
FP_ID | PR_ID|
------+------+
1 | 1 |
2 | 1 |
2 | 2 |
3 | 2 |
4 | 2 |
I am currently doing it recursively on app side, which may generate to many requests and hang the DB server, could this be done with some recursive DB approach?
Visualization:
Starting entity is marked by arrow.
Desired output is circled.
SQL Server solution
I guess the main problem is you need to compare by PR_ID then FP_ID. So in recursive part there must be a CASE statement. On 1 run we take data by FP_ID on second by PR_ID and etc with the help of modulo.
DECLARE #fp int = 1
;WITH cte AS (
SELECT f.FP_ID,
f.PR_ID,
1 as lev
FROM #FP_PR f
WHERE f.FP_id = #fp
UNION ALL
SELECT f.FP_ID,
f.PR_ID,
lev+1
FROM cte c
CROSS JOIN #FP_PR f -- You can use INNER JOIN instead
WHERE CASE (lev+1)%2 WHEN 0 THEN f.PR_ID WHEN 1 THEN f.FP_ID END = CASE (lev+1)%2 WHEN 0 THEN c.PR_ID WHEN 1 THEN c.FP_ID END
AND NOT (f.PR_ID = c.PR_ID AND f.FP_ID = c.FP_ID)
)
SELECT *
FROM cte
Output:
FP_ID PR_ID lev
1 1 1
2 1 2
2 2 3
3 2 4
4 2 4

TSQL SSRS Cross Reference another column

ID | Col2 | Col3 | SequenceNum
--------------------------------
1 | x | 12 | 5
2 | y | 11 | 6
3 | a | 45 | 7
100 | b | 23 | 8
101 | a | 16 | 9
102 | b | 28 | 10
4 | a | 9 | 11
5 | b | 26 | 12
6 | x | 100 | 13
I have an SSRS report at the moment which you can enter the ID for and it'll show you data for those ID's. For example lets say you enter start ID 2 end ID 5 it'll report back 2,3,4,5 with Col2 and Col3 data.
But what I really want to happen is for it to return 2,3,100,101,102,3,4,5
I believe may be some way to cross reference the SequenceNum column but I'm fairly new to SQL and SSRS can anyone help?
So an user would enter a parameters...
start-ID = 2 which has a SequenceNum of 6
and end-ID = 5 which has an SequenceNum of 12
Extract your starting and ending sequence numbers from value supplied by starting id and ending id respectively and use them in WHERE condition as below
DECLARE #StartingSeqNum INT, #EndingSeqNum
SELECT #StartingSeqNum = SeqNum FROM tableName WHERE ID = #start_id
SELECT #EndingSeqNum = SeqNum FROM tableName WHERE ID = #end_id
SELECT Col2,Col3
FROM tableName
WHERE SeqNum BETWEEN #StartingSeqNum AND #EndingSeqNum
As you are using SSRS you can specify a Value and a Label for your parameters.
Create a dataset with the following SQL as the source:
select distinct ID as Label
,SequenceNum as Value
from YourTable
order by SequenceNum
And then in the properties for your parameter, in Available Values select Get values from query and then select the above dataset. Set the Value field and Label field as your label and value columns and then click OK. You will need to do this for your start and end parameters, using the same dataset.
Your parameters will now be drop down menus that display the ID value to the user, but passes the SequenceNum value to your query. You can then use these to filter your main dataset.

How do I do a WHERE NOT IN for Hierarchical data?

I have a table that is a list of paths between points. I want to create a query to return a list with pointID and range(number of point) from a given point. But have spent a day trying to figure this out and haven't go any where, does any one know how this should be done? ( I am writing this for MS-SQL 2005)
example
-- fromPointID | toPointID |
---------------|-----------|
-- 1 | 2 |
-- 2 | 1 |
-- 1 | 3 |
-- 3 | 1 |
-- 2 | 3 |
-- 3 | 2 |
-- 4 | 2 |
-- 2 | 4 |
with PointRanges ([fromPointID], [toPointID], [Range])
AS
(
-- anchor member
SELECT [fromPointID],
[toPointID],
0 AS [Range]
FROM dbo.[Paths]
WHERE [toPointID] = 1
UNION ALL
-- recursive members
SELECT P.[fromPointID],
P.[toPointID],
[Range] + 1 AS [Range]
FROM dbo.[Paths] AS P
INNER JOIN PointRanges AS PR ON PR.[toPointID] = P.[fromPointID]
WHERE [Range] < 5 -- This part is just added to limit the size of the table being returned
--WHERE P.[fromPointID] NOT IN (SELECT [toPointID] FROM PointRanges)
--Cant do the where statment I want to because it wont allow recurssion in the sub query
)
SELECT * FROM PointRanges
--Want this returned
-- PointID | Range |
-----------|-------|
-- 1 | 0 |
-- 2 | 1 |
-- 3 | 1 |
-- 4 | 2 |
Markus Jarderot's link gives a good answer for this. I end tried using his answer and also tried rewriting my problem in C# and linq but it ended up being more of a mathematical problem than a coding problem because I had a table with several thousands of point that interlinked. This is still something I am interested in and trying to get a better understanding of by reading books on mathematics and graph theory but if anyone else runs into this problem I think Markus Jarderot's link is the best answer you will find.

Finding the difference between two sets of data from the same table

My data looks like:
run | line | checksum | group
-----------------------------
1 | 3 | 123 | 1
1 | 7 | 123 | 1
1 | 4 | 123 | 2
1 | 5 | 124 | 2
2 | 3 | 123 | 1
2 | 7 | 123 | 1
2 | 4 | 124 | 2
2 | 4 | 124 | 2
and I need a query that returns me the new entries in run 2
run | line | checksum | group
-----------------------------
2 | 4 | 124 | 2
2 | 4 | 124 | 2
I tried several things, but I never got to a satisfying answer.
In this case I'm using H2, but of course I'm interested in a general explanation that would help me to wrap my head around the concept.
EDIT:
OK, it's my first post here so please forgive if I didn't state the question precisely enough.
Basically given two run values (r1, r2, with r2 > r1) I want to determine which rows having row = r2 have a different line, checksum or group from any row where row = r1.
select * from yourtable
where run = 2 and checksum = (select max(checksum)
from yourtable)
Assuming your last run will have the higher run value than others, below SQL will help
select * from table1 t1
where t1.run in
(select max(t2.run) table1 t2)
Update:
Above SQL may not give you the right rows because your requirement is not so clear. But the overall idea is to fetch the rows based on the latest run parameters.
SELECT line, checksum, group
FROM TableX
WHERE run = 2
EXCEPT
SELECT line, checksum, group
FROM TableX
WHERE run = 1
or (with slightly different result):
SELECT *
FROM TableX x
WHERE run = 2
AND NOT EXISTS
( SELECT *
FROM TableX x2
WHERE run = 1
AND x2.line = x.line
AND x2.checksum = x.checksum
AND x2.group = x.group
)
A slightly different approach:
select min(run) run, line, checksum, group
from mytable
where run in (1,2)
group by line, checksum, group
having count(*)=1 and min(run)=2
Incidentally, I assume that the "group" column in your table isn't actually called group - this is a reserved word in SQL and would need to be enclosed in double quotes (or backticks or square brackets, depending on which RDBMS you are using).