T-SQL Recursive Select Circular dependency - sql-server-2008

I've got self dependant entities (a) in my database, which are referenced from another entity (b), and given a specific (b) entity, I need to get all the (a) entities that are needed. These are many to many mappings, so I have a separate mapping table. I think a recursive Select with a CTE is my best bet, but I'm running into an issue:
This Fiddle illustrates my issue. If some user introduces a circular reference, my recursive select grinds to a screeching halt. I've been wracking my brain to try to find some way to fix this. It should be noted that though I have introduced Foreign Keys in the fiddle, foreign keys aren't actually honored by the system I'm using (long standing argument with the DBAs) - I introduced them to make data flow more clear.
The recursive query, for those who don't want to click through to the fiddle:
WITH recur(objID) AS (
SELECT usesObjID
FROM #otherObj
WHERE otherObjID = 1
UNION ALL
SELECT slaveObjID
FROM #objMap
INNER JOIN recur
on #objMap.masterObjID = recur.objID
)SELECT objID from recur
Any Ideas out there? This design isn't in production, so I can change schema somewhat, but I'd like not to rely on discovering circular references upon insertion, unless it can be done by T-SQL.

It's possible to set the MAXRECURSION of the CTE, which will prevent the infinite loop, but you'll still get weird results since the query will continue to run in the loop until the max recursion is hit.
The challenge is that the loop involves multiple steps, so you can't just check the immediate parent of the child in order to determine if you're in a loop.
One way to handle this would be to add an additional column to the CTE... this new column, tree, tracks all the IDs that have been included so far, and stops when an ID repeats.
WITH recur(objID, Tree) AS (
SELECT
usesObjID,
CAST(',' + CAST(usesObjID AS VARCHAR) + ',' AS VARCHAR) AS Tree
FROM otherObj
WHERE otherObjID = 1
UNION ALL
SELECT
slaveObjID,
CAST(recur.Tree + CAST(slaveObjID AS VARCHAR) + ',' AS VARCHAR) AS Tree
FROM objMap
INNER JOIN recur
ON objMap.masterObjID = recur.objID
WHERE recur.Tree NOT LIKE '%,' + CAST(slaveObjID AS VARCHAR) + ',%'
)SELECT objID from recur
Sql Fiddle Link

Related

SQL - is it possible to call variables even if it's different table?

So I have a table
- Members - store the parents
- Child - restore the child
I'm new to sql and my code is not working as expect, but you might be able to understand what I'm trying to accomplish here.
set #variable1 = (select idMembers from members where firstname like '%James%')
set #variable2 = (select FirstName, lastname, relationship from child where idMembers = #variable)
print #variable2
I am recommending you to use JOIN to extract value from multiple related tables. However, as in your case, you are actually assign multiple column value to "variable2" which may be the problem.
BTW, you may want to read this topic to see the difference between SET and SELECT.

Replacing existing View but MySQL says "Table doesn't exist"

I have a table in my MySQL database, compatibility_core_rules, which essentially stores pairs of ids which represent compatibility between parts which have fields with those corresponding ids. Now, my aim is to get all possible compatibility pairs by following the transitivity of the pairs (e.g. so if the table has (1,2) and (2,4), then add the pair (1,4)). So, mathematically speaking, I'm trying to find the transitive closure of the compatibility_core_rules table.
E.g. if compatibility_core_rules contains (1,2), (2,4) and (4,9), then initially we can see that (1,2) and (2,4) gives a new pair (1,4). I then iterate over the updated pairs and find that (4,9) with the newly added (1,4) gives me (1,9). At this point, iterating again would add no more pairs.
So my approach is to create a view with the initial pairs from compatibility_core_rules, like so:
CREATE VIEW compatibility_core_rules_closure
AS
SELECT part_type_field_values_id_a,
part_type_field_values_id_b,
custom_builder_id
FROM compatibility_core_rules;
Then, in order to iteratively discover all pairs, I need to keep replacing that view with an updated version of itself that has additional pairs each time. However, I found MySQL doesn't like me referencing the view in its own definition, so I make a temporary view (with or replace, since this will be inside a loop):
CREATE OR REPLACE VIEW compatibility_core_rules_closure_temp
AS
SELECT part_type_field_values_id_a,
part_type_field_values_id_b,
custom_builder_id
FROM compatibility_core_rules_closure;
No problems here. I then reference this temporary view in the following CREATE OR REPLACE VIEW statement to update the compatibility_core_rules_closure view with one iteration's worth of additional pairs:
CREATE OR REPLACE VIEW compatibility_core_rules_closure
AS
SELECT
CASE WHEN ccr1.part_type_field_values_id_a = ccr2.part_type_field_values_id_a THEN ccr1.part_type_field_values_id_b
WHEN ccr1.part_type_field_values_id_a = ccr2.part_type_field_values_id_b THEN ccr1.part_type_field_values_id_b
END ccrA,
CASE WHEN ccr1.part_type_field_values_id_a = ccr2.part_type_field_values_id_a THEN ccr2.part_type_field_values_id_b
WHEN ccr1.part_type_field_values_id_a = ccr2.part_type_field_values_id_b THEN ccr2.part_type_field_values_id_a
END ccrB,
ccr1.custom_builder_id custom_builder_id
FROM compatibility_core_rules_closure_temp ccr1
INNER JOIN compatibility_core_rules_closure_temp ccr2
ON (
ccr1.part_type_field_values_id_a = ccr2.part_type_field_values_id_a OR
ccr1.part_type_field_values_id_a = ccr2.part_type_field_values_id_b
)
GROUP BY ccrA,
ccrB
HAVING -- ccrA and ccrB are in fact not the same
ccrA != ccrB
-- ccrA and ccrB do not belong to the same part type
AND (
SELECT ptf.part_type_id
FROM part_type_field_values ptfv
INNER JOIN part_type_fields ptf
ON ptfv.part_type_field_id = ptf.id
WHERE ptfv.id = ccrA
LIMIT 1
) !=
(
SELECT ptf.part_type_id
FROM part_type_field_values ptfv
INNER JOIN part_type_fields ptf
ON ptfv.part_type_field_id = ptf.id
WHERE ptfv.id = ccrB
LIMIT 1
)
Now this is where things go wrong. I get the following error:
#1146 - Table 'db509574872.compatibility_core_rules_closure' doesn't exist
I'm very confused by this error message. I literally just created the view/table only two statements ago. I'm sure the SELECT query itself is correct since if I try it by itself and it runs fine. If I change the first line to use compatibility_core_rules_closure2 instead of compatibility_core_rules_closure then it runs fine (however, that's not much use since I need to be re-updating the same view again and again). I've looked into the SQL SECURITY clauses but have not had any success. Also been researching online but not getting anywhere.
Does anyone have any ideas what is happening and how to solve it?
MySQL doesn't support sub-queries in views.
You'll have to separate them... ie. using another view containing the sub-query inside you main view.
Running the create statement for that view will render an error, not creating it, hence the doesn't exist error you're getting when querying it.

I am probably Locking my SQL Server Table by running a SELECT and INSERT and SELECT concurrently

I have a table in SQL Server 2008 R2 that holds "positions" for different companies.
I have a function GET_KEY that creates a position number by performing a SELECT MAX over the table. [I can not use Identity Increment because the position number needs to be the first available number for a particular company]
SELECT #v_position_no = max(POSITION_NO) + 0.01
FROM POSITIONS WHERE COMPANY_NO = #p_company_no
I have a function that copies a position COPY_POS.
I want to copy an existing position twice, but to assign each position an appropriate position number.
I run GET_KEY, then COPY_POS, then GET_KEY, then COPY_POS
However, the positions table locks. I am almost positive (and it is logical) that this is because there a conflict between GET_KEY and COPY_POS. [The problem only occurs if I try and do GET_KEY and COPY_POS twice in succession]
What I tried...
I tried encasing each of GET_KEY and COPY_POS in a BEGIN TRAN and COMMIT TRAN, but this didn't work.
The entire stored procedure is contained in a BEGIN TRY and END TRY and I tried removing this, but this also didn't work...
Does anyone have any ideas?
Thank you!
Simply add a WITH (nolock) to the Select statement!
SELECT #v_position_no = max(POSITION_NO) + 0.01
FROM POSITIONS WITH (nolock) WHERE COMPANY_NO = #p_company_no

SQL Server 2008 SELECT * FROM #variable?

It is possible?
DECLARE #vTableName varchar(50)
SET #vTableName = (SELECT TableName FROM qms_Types WHERE Id = 1)
SELECT * FROM #vTableName
I have this error:
Msg 1087, Level 16, State 1, Line 3 Must declare the table variable
"#vTableName".
Short answer: No.
Long answer: Noooooooooooooooooooooooooooooooooooooo. Use dynamic SQL if you have to, but if you're structuring your tables in a way where you don't know the table name ahead of time, it might benefit you to rethink your schema.
Here is a great resource for learning how to use dynamic SQL: The Curse and Blessings of Dynamic SQL
if you're trying to select from a table of that name, then you can do something like this:
DECLARE #vTableName varchar(50)
SET #vTableName = (SELECT TableName FROM qms_Types WHERE Id = 1)
EXECUTE('SELECT * FROM [' + #vTableName + ']')
my solution for this:
EXECUTE('SELECT * FROM ' + TableName + '')
It seems as though different folks are interpreting the OP differently.
I'm pretty sure the OP is asking for this type of concept / ability / maneuver...
"Put a table name into a variable and then use that variable as though it were a table name."
DECLARE #TableIWantRecordsFrom varchar(50)
-- ^^^^^^^^^^^^^^^^^^^^^^
SET #TableIWantRecordsFrom = (SELECT TableName FROM qms_Types WHERE Id = 1) -- (L1)
-- ^^^^^^^^^^^^^^^^^^^^^^
-- Let's say, at this point, #TableIWantRecordsFrom ... contains the text 'Person'
-- ^^^^^^^^^^^^^^^^^^^^^^
-- assuming that is the case then...
-- these two queries are supposed to return the same results:
SELECT top 3 fname,lname,mi,department,floor FROM Person
-- ^^^^^^
SELECT top 3 fname,lname,mi,department,floor FROM #TableIWantRecordsFrom -- (L2)
-- ^^^^^^^^^^^^^^^^^^^^^^
From reading all the responses and answers, it appears that this kind of maneuver can't be done - unless - you use dynamic SQL which...
can be a bit of a pain to create and maintain and
can be more work to create than the time it "saves" you in the future.
================================================================
There are other languages where this can be done... in literally, two lines of code (see (L1) and (L2) in above code) and not having to do a lot of formatting and editing.)
(I've done it before - there is another language where all you'd need is L1 and L2...)
================================================================
It is unfortunate that SQL Server will not do this without going to a decent amount of effort...
first write your SQL then
test it to make sure it does, in fact, work then
frame each line with tick marks and then escape your ticks that are now inside THOSE tick marks
declare the variable
set the variable to the sql statement you ticked above
(I may be missing some additional steps)
Oh, and then, if you ever need to maintain it
you need to either, be very careful and just edit it right there, as is, and hope you get it all just right -or- you may have saved a copy of it... un-ticked and un-variablized so you can edit the "real" sql and then when you're done you can RE DO these steps... again.
I think you want this:
DECLARE #vTableName table(TableName varchar(50))
insert into #vTableName
SELECT TableName FROM qms_Types WHERE Id = 1
SELECT * FROM #vTableName
The only way you can do this is through Dynamic SQL which refers to the practice of creating a T-SQL text and executing it using the sp_executesql (or simply exec)
Here is a helpful link about dynamic sql The Curse and Blessings of Dynamic SQL.
You should really think whether or not this is a case for dynamic sql or if there is another way for you to perform this operation.

Combine 'like' and 'in' in a SqlServer Reporting Services query?

The following doesn't work, but something like this is what I'm looking for.
select *
from Products
where Description like (#SearchedDescription + %)
SSRS uses the # operator in-front of a parameter to simulate an 'in', and I'm not finding a way to match up a string to a list of strings.
There are a few options on how to use a LIKE operator with a parameter.
OPTION 1
If you add the % to the parameter value, then you can customize how the LIKE filter will be processed. For instance, your query could be:
SELECT name
FROM master.dbo.sysobjects
WHERE name LIKE #ReportParameter1
For the data set to use the LIKE statement properly, then you could use a parameter value like sysa%. When I tested a sample report in SSRS 2008 using this code, I returned the following four tables:
sysallocunits
sysaudacts
sysasymkeys
sysaltfiles
OPTION 2
Another way to do this that doesn't require the user to add any '%' symbol is to generate a variable that has the code and exceute the variable.
DECLARE #DynamicSQL NVARCHAR(MAX)
SET #DynamicSQL =
'SELECT name, id, xtype
FROM dbo.sysobjects
WHERE name LIKE ''' + #ReportParameter1 + '%''
'
EXEC (#DynamicSQL)
This will give you finer controller over how the LIKE statement will be used. If you don't want users to inject any additional operators, then you can always add code to strip out non alpha-numeric characters before merging it into the final query.
OPTION 3
You can create a stored procedure that controls this functionality. I generally prefer to use stored procedures as data sources for SSRS and never allow dynamically generated SQL, but that's just a preference of mine. This helps with discoverability when performing dependency analysis checks and also allows you to ensure optimal query performance.
OPTION 4
Create a .NET code assembly that helps dynamically generate the SQL code. I think this is overkill and a poor choice at best, but it could work conceivably.
Have you tried to do:
select * from Products where Description like (#SearchedDescription + '%')
(Putting single quotes around the % sign?)
Dano, which version of SSRS are you using? If it's RS2000, the multi-parameter list is
not officially supported, but there is a workaround....
put like this:
select *
from tsStudent
where studentName like #SName+'%'
I know this is super old, but this came up in my search to solve the same problem, and I wound up using a solution not described here. I'm adding a new potential solution to help whomever else might follow.
As written, this solution only works in SQL Server 2016 and later, but can be adapted for older versions by writing a custom string_split UDF, and by using a subquery instead of a CTE.
First, map your #SearchedDescription into your Dataset as a single string using JOIN:
=JOIN(#SearchedDedscription, ",")
Then use STRING_SPLIT to map your "A,B,C,D" kind of string into a tabular structure.
;with
SearchTerms as (
select distinct
Value
from
string_split(#SearchedDescription, ',')
)
select distinct
*
from
Products
inner join SearchTerms on
Products.Description like SearchTerms.Value + '%'
If someone adds the same search term multiple times, this would duplicate rows in the result set. Similarly, a single product could match multiple search terms. I've added distinct to both the SearchTerms CTE and the main query to try to suppress this inappropriate row duplication.
If your query is more complex (including results from other joins) then this could become an increasingly big problem. Just be aware of it, it's the main drawback of this method.