Duplicate record by using with CTEs SQL Server 2008 - asp.net

I need to manage hierarchy data storing in my database. But I have a problem now. Please see my example
I have a table called COMMON.TASK_REL
My second table is called Common. task
I suppose need to sort the task_seq and return a result like below:
Task Name || Task_Seq
Item1 1
..Item1.2 1
...Item1.2.1 1
..Item1.1 2
Here is my query
--Common task SQL modify --
WITH ctLevel
AS
(
SELECT
C_TASK_ID AS Child
,P_Task_ID AS Parent
,common_task.TASK_SEQ AS taskOrder
,1 AS [Level]
,CAST(C_TASK_ID AS VARCHAR(MAX)) AS [Order]
,CAST (Replicate('.', 1) + common_task.TASK_NAME AS VARCHAR(25)) AS [Task_Name]
FROM
[COMMON.TASK_REL] as common_task_rel,
[COMMON.TASK] as common_task
WHERE common_task_rel.C_TASK_ID = common_task.TASK_ID
and common_task.[TASK_TYPE] = 'B' AND common_task.[MODULE_CODE] = 'LWRPT'
AND common_task.[STATUS] <> 'D'
UNION ALL
SELECT
C_TASK_ID AS Child
,P_Task_ID AS Parent
,common_task.TASK_SEQ AS taskOrder
,[Level] + 1 AS [Level]
,[Order] + '.' + CAST(C_TASK_ID AS VARCHAR(MAX)) AS [Order]
,CAST (Replicate('.', [Level] + 1) + common_task.TASK_NAME AS VARCHAR(25)) AS [Task_Name]
FROM [COMMON.TASK_REL] as common_task_rel
INNER JOIN ctLevel
ON ( P_Task_ID = Child ) , [COMMON.TASK] as common_task
WHERE common_task_rel.C_TASK_ID = common_task.TASK_ID
and common_task.[TASK_TYPE] = 'B' AND common_task.[MODULE_CODE] = 'LWRPT'
AND common_task.[STATUS] <> 'D'
)
-- Viewing Data
SELECT Child ,Parent ,taskOrder,Level,[Order],Task_Name
FROM ctLevel
GROUP BY Child ,Parent ,taskOrder,Level,[Order],Task_Name
order by [Order];
GO
But my result returns duplicated rows:
Anyone can help me correct my query? Thanks

I believe that your duplicates are coming from your root/anchor query. You should add the following to that query:
AND Task_Seq = 0
Basically, you only want the root to be set up as the beginning of the tree. 301|300 should not be picked up until the recursion section (the part after union all)
If that does not make sense, then I can repaste your query with the modification, but that seemed unnecessary for a small change.

Related

sqlite3 : Compare two cells from the same table

I have a table like this
CREATE TABLE "modules" ( `ID` INTEGER PRIMARY KEY AUTOINCREMENT, `name` TEXT, `version` TEXT, `deployID` INTEGER )
I want to be able to get all the rows from this database where the version is different between different deploy ID's. So, say I have a deploy ID 2 and a deployID 3 - my table might have 2000 odd modules for each of these deploys. I want to only get the rows where the name is the same but the version is different. Is this possible? I thought this query would do it but it seems to be returning me everything - twice!
SELECT a.* FROM modules a
INNER JOIN modules b
ON a.name == b.name
WHERE a.version != b.version
AND a.deployID = 3
AND b.deployID = 2
If you only care about deployids 2 and 3:
select m.*
from modules m
where
m.deployid in (2, 3)
and
exists (
select 1 from modules
where
name = m.name
and
deployID <> m.deployID
and
version <> m.version
)
If you don't need this condition:
deployID <> m.deployID
you can remove it.
I would probably use an exists query here:
SELECT m1.*
FROM modules m1
WHERE EXISTS (SELECT 1 FROM modules m2
WHERE m1.name = m2.name AND
m1.deployID <> m2.deployID AND m1.version <> m2.version);
Or maybe you want a more specific version:
SELECT m1.*
FROM modules m1
WHERE EXISTS (SELECT 1 FROM modules m2
WHERE m1.name = m2.name AND
LEAST(m1.deployID, m2.deployID) = 2 AND
GREATEST(m1.deployID, m2.deployID) = 3);

updating Hierarchy Level

We have a single table that includes Parent/Child relationships.
We are trying to update the Level of the Children based on the Parents Level + 1
We are currently doing a loop in code that looks something like this:
Dim ParentLevel as Integer = 1
Do While Count > 0
Update [MyTable]
SET [MyLevel] = (SELECT [P].[MyLevel] + 1
FROM [MyTable] [P]
WHERE [MyTable].[Parent] = [P].[Child]
AND [P].[MyLevel] = ParentLevel)
WHERE [MyLevel] IS NULL
AND [Parent] IN (SELECT [Child]
FROM [MyTable] [P]
WHERE P.[MyLevel] = ParentLevel);
execute query
ParentLevel = ParentLevel + 1
Loop
This table has only 31148 records.
When ParentLevel is below around 3 or 4 the update query's performance is acceptable.
However after that it takes to long. Therefore we know we must be doing some wrong.
Indexes are on parent and Child and MyLevel.
We've tried other update queries like below but performance is the same:
Update [MyTable]
SET [MyLevel] = (SELECT P.[MyLevel] + 1
FROM [MyTable] AS P
WHERE [MyTable].[Parent] = P.[Child]
AND [P].[MyLevel] = ParentLevel )
WHERE EXISTS (SELECT P.[MyLevel] + 1
FROM [MyTable] AS P
WHERE [MyTable].[Parent] = P.[Child]
AND [P].[MyLevel] = 6);
We were hoping for some SQLite experts advice on a better performing update query.

Oracle - Connect By Clause Required in query block

I would like to utilize the Month Column in the below syntax in a Case Statement. When I create a sub query I receive the Oracle error 01788 Connect By Clause Required in query block. How can one utilize the Month column in the case statment in the subquery?
TO_CHAR(ADD_MONTHS(TRUNC(StartDate, 'MM'), LEVEL - 1), 'YYYYMM') AS Month
Query below:
SELECT
CASE
WHEN first_assgn_dt_YYYYMM <= Month
THEN 0
WHEN EndDate < LAST_DAY(EndDate) AND EndDate != sysdate
AND LEVEL = 1 + MONTHS_BETWEEN(TRUNC(EndDate,'MM'),TRUNC(StartDate,'MM'))
THEN 0
ELSE 1
END AS active_at_month_end
FROM (
WITH
ActiveMemberData (ID,StartDate,EndDate,first_assgn_dt,first_assgn_dt_YYYYMM) AS (
SELECT DISTINCT
x.ID,
TRUNC(x.start_dt) AS StartDate,
CASE WHEN TRUNC(X.END_DT) = '1-JAN-3000' THEN SYSDATE ELSE TO_DATE(X.END_DT) END AS EndDate,
x.first_assgn_dt,
TO_CHAR(first_assgn_dt,'YYYYMM') AS first_assgn_dt_YYYYMM
FROM X
LEFT JOIN D ON X.MID = D.ID
WHERE 1=1
)
--------------------------------------------------
SELECT DISTINCT
ID,
first_assgn_dt,
first_assgn_dt_YYYYMM,
StartDate,
TO_CHAR(StartDate,'YYYYMM') AS StartDate_YYYYMM,
EndDate,
TO_CHAR(ADD_MONTHS(TRUNC(StartDate, 'MM'), LEVEL - 1), 'YYYYMM') AS Month,
LAST_DAY(EndDate) AS LastDayOfMonth
FROM ActiveMemberData
WHERE 1=1
------------------------------------------------------------------------------------
CONNECT BY LEVEL <= 1 + MONTHS_BETWEEN(TRUNC(EndDate,'MM'), TRUNC(StartDate,'MM'))
AND PRIOR ID = ID AND PRIOR STARTDATE = STARTDATE
AND PRIOR sys_guid() IS NOT NULL
) Z
WHERE 1=1
ORDER BY
ID,
Month
That has nothing to do with trying to refer to Month from the inline view; that is fine. It's the separate reference to level that is causing the error.
If you want to be able to see the level from your inline view in the outer query, as you are with this line:
AND LEVEL = 1 + MONTHS_BETWEEN(TRUNC(EndDate,'MM'),TRUNC(StartDate,'MM'))
then you have to include it in the select list - with an alias - and then refer to that alias:
SELECT
CASE
...
AND LEVEL_ALIAS = 1 + MONTHS_BETWEEN(TRUNC(EndDate,'MM'),TRUNC(StartDate,'MM'))
...
FROM (
...
SELECT DISTINCT
LEVEL as LEVEL_ALIAS,
ID,
...
You can call the alias whatever you want, of course; you just can't use the reserved word level.
Anything you want visible in the outer query always has to be in the inline view's select list - but usually you can keep the original column name; you have to use an alias for an expression or a pseucocolumn though, which is the case here.
You don't have to use an alias for the reserved word. Just add double quotes and capitilise it i.e. "LEVEL"

Cannot detect cyclic data in an SQLite database

Using SQLite, I have a suspected case of recursive data that has cycle - in other words, a child node is also its own grandparent. The symptom, of course, is an infinite loop
I know how Oracle and Postgresql handle cyclic data; but have not found any method to do so with SQLite. Below is an example of data with a cycle. If you remove the last "row" in the dataset table, it will work. As written, it goes into an infinite loop.
with DataSet as
(
select 'A' as node, null as parent union all
select 'B' as node, 'A' as parent union all
select 'C' as node, 'B' as parent union all
select 'D' as node, 'C' as parent union all
select 'A' as node, 'D' as parent
),
Hierarchy( node, parent, level, path )
as
(
select DataSet.node,
DataSet.parent,
1 as level,
'/' || DataSet.node as path
from DataSet
where DataSet.parent is null
union all
select DataSet.node,
DataSet.parent,
Hierarchy.level + 1 as level,
Hierarchy.path || '/' || DataSet.node as path
from Hierarchy
join DataSet
on DataSet.parent = Hierarchy.node
)
select *
from Hierarchy
order by path
;
Without the level, you could use UNION to ignore the duplicates.
Otherwise, there is no easy way to compare 'old' rows.
You could add a sufficiently large LIMIT to prevent the loop from becoming infinite, but then it is not always possible to remove duplicates.
To close the loop (pun intended) on this, I finally figured out a method that works. Plus I even added a column to show the rows where a cyclic condition exists. This solution includes: level (depth) number, parent, child, path (root to node/leaf), and a zero or one for detection of a cycle. You can paste the SQL below into the sqlite3 prompt and it will display:
level parent node path cyclic_flag
------ ------- ------- ------------ ------------
0 A B /A/B 0
1 B C /A/B/C 0
2 C D /A/B/C/D 0
3 D A /A/B/C/D/A 1
sqlite>
Finally, here is the SQL that shows the method. The only tricky part is the use of counting substrings in the path (the next node being the substring counted).
with dataset as
(
select 'A' as parent, 'B' as node union all
select 'B' as parent, 'C' as node union all
select 'C' as parent, 'D' as node
union all select 'D' as parent, 'A' as node
),
hierarchy( level, parent, node, path, cyclic_flag )
as
(
select 0 as level,
dataset.parent,
dataset.node,
'/' || dataset.parent || '/' || dataset.node as path,
0 as cyclic_flag
from dataset
where dataset.parent = 'A'
union all
select
hierarchy.level + 1 as level,
dataset.parent,
dataset.node,
hierarchy.path || '/' || dataset.node as path,
case
when
(length(path||dataset.node) - length(replace(path|| dataset.node,dataset.node,'')))/length(dataset.node) = 1
then 0
else 1
end as cyclic_flag
from hierarchy
inner join dataset
on dataset.parent = hierarchy.node
where (length(path) - length(replace(path,hierarchy.node,'')))/length(hierarchy.node) < 2
)
select *
from hierarchy
order by path
;

issue in sql Query

I have an column in table where this column name is items it contains value like this
itemID items
1 school,college
2 place, country
3 college,cricket
4 School,us,college
5 cricket,country,place
6 football,tennis,place
7 names,tennis,cricket
8 sports,tennis
Now I need to write a search query
Ex: if the user types 'cricket' into a textbox and clicks the button I need to check in the column items for cricket.
In the table I have 3 rows with cricket in the items column (ItemId = 3, 5, 7)
If the user types in tennis,cricket then I need to get the records that match either one. So I need to get 5 row (ItemId = 3, 5, 6, 7, 8)
How do I write a query for this requirement?
You need to start by redesigning your database as this is is a very bad structure. You NEVER store a comma delimited list in a field. First think about waht fields you need and then design a proper database.
The very bad structure of this table (holding multiple values in one column) is the reason you are facing this issue. Your best option is to normalize the table.
But if you can't, then you can use the "Like" operator, with a wildcard
Select * From Table
Where items Like '%cricket%'
or
Select * From Table
Where items Like '%cricket%'
or items Like '%tenis%'
You will need to dynamically construct these sql queries from the inputs the user makes. The other alternative is to write code on the server to turn the comma delimited list of parameters into a table variable or temp table and then join to it..
Delimited values in columns is almost always a bad table design. Fix your table structure.
If for some reason you are unable to do that, the best you can hope for is this:
SELECT * FROM [MyTable] WHERE items LIKE '%CRICKET%'
This is still very bad, for two important reasons:
Correctness. It would return values that only contain the word cricket. Using your tennis example, what if you also had a "tennis shoes" item?
Performance. It's not sargable, which means the query won't work with any indexes you may have on that column. That means your query will probably be incredibly slow.
If you need help fixing this structure, the solution is to add another table — we'll call it TableItems — with a column for your ItemID that will be a foreign key to your original table and an item field (singular) for each of your item values. Then you can join to that table and match a column value exactly. If these items work more like categories, where you want to rows with the "Cricket" item to match the same cricket item, you also want a third table to be an intersection between your original table and the other one I just had you create.
For a single item:
SELECT itemID, items FROM MyTable WHERE items LIKE '%cricket%'
For multiple items:
SELECT itemID, items FROM MyTable WHERE items LIKE '%tennis%' or items LIKE '%cricket%'
You'll need to parse the input and split them up and add each item to the query:
items LIKE '%item1%' or items LIKE '%item2%' or items LIKE '%item3%' ...
I think that in the interest of validity of data, it should be normalized so that you split the Items into a separate table with an item on each row.
In either case, here is a working sample that uses a user defined function to split the incoming string into a Table Variable and then uses JOIN with a LIKE
CREATE FUNCTION dbo.udf_ItemParse
(
#Input VARCHAR(8000),
#Delimeter char(1)='|'
)
RETURNS #ItemList TABLE
(
Item VARCHAR(50) ,
Pos int
)
AS
BEGIN
DECLARE #Item varchar(50)
DECLARE #StartPos int, #Length int
DECLARE #Pos int
SET #Pos = 0
WHILE LEN(#Input) > 0
BEGIN
SET #StartPos = CHARINDEX(#Delimeter, #Input)
IF #StartPos < 0 SET #StartPos = 0
SET #Length = LEN(#Input) - #StartPos - 1
IF #Length < 0 SET #Length = 0
IF #StartPos > 0
BEGIN
SET #Pos = #Pos + 1
SET #Item = SUBSTRING(#Input, 1, #StartPos - 1)
SET #Input = SUBSTRING(#Input, #StartPos + 1, LEN(#Input) - #StartPos)
END
ELSE
BEGIN
SET #Pos = #Pos+1
SET #Item = #Input
SET #Input = ''
END
INSERT #ItemList (Item, Pos) VALUES(#Item, #Pos)
END
RETURN
END
GO
DECLARE #Itemstable TABLE
(
ItemId INT,
Items VarChar (1000)
)
INSERT INTO #Itemstable
SELECT 1 itemID, 'school,college' items UNION
SELECT 2, 'place, country' UNION
SELECT 3, 'college,cricket' UNION
SELECT 4, 'School,us,college' UNION
SELECT 5, 'cricket,country,place' UNION
SELECT 6, 'footbal,tenis,place' UNION
SELECT 7, 'names,tenis,cricket' UNION
SELECT 8, 'sports,tenis'
DECLARE #SearchParameter VarChar (100)
SET #SearchParameter = 'cricket'
SELECT DISTINCT ItemsTable.*
FROM #Itemstable ItemsTable
INNER JOIN udf_ItemParse (#SearchParameter, ',') udf
ON ItemsTable.Items LIKE '%' + udf.Item + '%'
SET #SearchParameter = 'cricket,tenis'
SELECT DISTINCT ItemsTable.*
FROM #Itemstable ItemsTable
INNER JOIN udf_ItemParse (#SearchParameter, ',') udf
ON ItemsTable.Items LIKE '%' + udf.Item + '%'
Why exactly are you using a database in the first place?
I mean : you are clearly not using it's potential. If you like using comma separated stuff, try a file.
In MySQL, create a fulltext index on your table:
CREATE FULLTEXT INDEX fx_mytable_items ON mytable (items)
and issue this query:
SELECT *
FROM mytable
WHERE MATCH(items) AGAINST ('cricket tennis' IN BOOLEAN MODE)

Resources