I'm new here, so please accept my apologize if there is a rule that I ignored it.
I use SQL Server 2005.
I have a table which contains a parent-child structured tree.
There is a query that scans above mentioned tree. There is also another tree including some of children of the 1st table. Each child of 1st table could be repeated for more than 1 time in the 2nd table.
1st table (called TBL1):
CID Parent_ID Seq_NO CName
650 NULL 1 A
135 650 1 B
950 135 1 C
124 135 2 D
725 135 3 E
421 135 4 F
632 421 1 G
906 421 2 H
119 421 3 I
215 650 2 J
436 215 1 K
150 215 2 L
260 150 1 M
501 260 1 N
154 260 2 O
132 260 3 P
721 150 2 Q
960 215 3 R
Query for scannning the tree of TBL1
WITH SCAN_TREE(IID, QName, Parent_ID, levleTREE, HRName, SO) AS
(
SELECT
CID, CName, Parent_ID,
0 AS initlvl,
CAST(INIT_POINT.CName AS VARCHAR(MAX)) AS initName,
CAST(INIT_POINT.Seq_NO AS VARBINARY(MAX)) AS initSO
FROM TBL1 AS INIT_POINT
WHERE (INIT_POINT.CID=650)
UNION ALL
SELECT
LOOP_Q.CID, LOOP_Q.CName, LOOP_Q.Parent_ID,
FINAL_Q.levleTREE + 1 AS looplvl,
CAST(FINAL_Q.HRName + '-' + LOOP_Q.CName AS VARCHAR(MAX)) AS loopName,
CAST(FINAL_Q.SO + CAST(LOOP_Q.Seq_NO AS BINARY(4)) AS VARBINARY(MAX)) AS loopSO
FROM TBL1 AS LOOP_Q
INNER JOIN SCAN_TREE AS FINAL_Q ON LOOP_Q.Parent_ID = FINAL_Q.IID
)
SELECT
LAST_Q.IID, LAST_Q.QName, LAST_Q.Parent_ID, LAST_Q.levleTREE, LAST_Q.HRName
FROM SCAN_TREE AS LAST_Q
ORDER BY SO
The output of above query:*
IID QName Parent_ID levleTREE HRName
650 A NULL 0 A
135 B 650 1 A-B
950 C 135 2 A-B-C
124 D 135 2 A-B-D
725 E 135 2 A-B-E
421 F 135 2 A-B-F
632 G 421 3 A-B-F-G
906 H 421 3 A-B-F-H
119 I 421 3 A-B-F-I
215 J 650 1 A-J
436 K 215 2 A-J-K
150 L 215 2 A-J-L
260 M 150 3 A-J-L-M
501 N 260 4 A-J-L-M-N
154 O 260 4 A-J-L-M-O
132 P 260 4 A-J-L-M-P
721 Q 150 3 A-J-L-Q
960 R 215 2 A-J-R
2nd table (called TBL2):
MID
----
950
124
124
632v
632
632
421
What I want is the sum of all occurance of each child of 1nd table in 2nd table for each parent of 1st table.
I need a query to retrieve below result, actually I need first column (MID):
MID IID QName Parent_ID levleTREE HRName
7 650 A NULL 0 A
7 135 B 650 1 A-B
1 950 C 135 2 A-B-C
2 124 D 135 2 A-B-D
0 725 E 135 2 A-B-E
4 421 F 135 2 A-B-F
3 632 G 421 3 A-B-F-G
0 906 H 421 3 A-B-F-H
0 119 I 421 3 A-B-F-I
0 215 J 650 1 A-J
0 436 K 215 2 A-J-K
0 150 L 215 2 A-J-L
0 260 M 150 3 A-J-L-M
0 501 N 260 4 A-J-L-M-N
0 154 O 260 4 A-J-L-M-O
0 132 P 260 4 A-J-L-M-P
0 721 Q 150 3 A-J-L-Q
0 960 R 215 2 A-J-R
Thank you in advance.
You can do a few standard left joins here:
WITH SCAN_TREE(IID, QName, Parent_ID, levleTREE, HRName, SO, MID) AS
(
SELECT
CID,
CName,
Parent_ID,
0 AS initlvl,
CAST(INIT_POINT.CName AS VARCHAR(MAX)) AS initName,
CAST(INIT_POINT.Seq_NO AS VARBINARY(MAX)) AS initSO,
COUNT(T2.MID) as MID
FROM
TBL1 AS INIT_POINT
LEFT JOIN TBL2 AS T2 ON
INIT_POINT.CID = T2.MID
WHERE
INIT_POINT.CID=650
GROUP BY
CID,
CName,
ParentId,
Seq_NO
UNION ALL
SELECT
LOOP_Q.CID,
LOOP_Q.CName,
LOOP_Q.Parent_ID,
FINAL_Q.levleTREE + 1 AS looplvl,
CAST(FINAL_Q.HRName + '-' + LOOP_Q.CName AS VARCHAR(MAX)) AS loopName,
CAST(FINAL_Q.SO + CAST(LOOP_Q.Seq_NO AS BINARY(4)) AS VARBINARY(MAX)) AS loopSO,
COUNT(T2.MID) as MID
FROM
TBL1 AS LOOP_Q
INNER JOIN SCAN_TREE AS FINAL_Q ON
LOOP_Q.Parent_ID = FINAL_Q.IID
LEFT JOIN TBL2 AS T2 ON
LOOP_Q.CID = T2.MID
GROUP BY
LOOP_Q.CID,
LOOP_Q.CName,
LOOP_Q.Parent_ID,
FINAL_Q.levleTREE,
FINAL_Q.HRName,
FINAL_Q.SO,
FINAL_Q.Seq_NO
)
SELECT
LAST_Q.IID,
LAST_Q.QName,
LAST_Q.Parent_ID,
LAST_Q.levleTREE,
LAST_Q.HRName,
SUM(T2.MID) as MID
FROM
SCAN_TREE AS LAST_Q
LEFT JOIN SCAN_TREE as CHILDREN ON
CHILDREN.HRName like LAST_Q.HRName + '%'
GROUP BY
LAST_Q.IID,
LAST_Q.QName,
LAST_Q.Parent_ID,
LAST_Q.levleTREE,
LAST_Q.HRName
order by SO
All we're doing is joining TBL2 to TBL1 based on when the CID column equals the MID column. Then, we're counting the number of results we get. Since a left join will give us a null value for the MID column if one doesn't exist, we can rely on this for counting--null equals 0 to count. Next, outside of the CTE, we just join the CTE to itself and sum up those counts where the HRName (a nice hierarchy) begins with the given HRName, so A-B-C will grab the MID of A-B-C, A-B-C-D, A-B-C-D-E, and A-B-C-J, for example.
Related
I have a table with 4 column in sql (key ID):
ID Items Import Export
1 A1 1333 0
2 A1 0 368
3 A1 0 252
4 A1 1965 0
5 A1 0 162
6 A1 0 551
7 A1 0 69
I want calculate inventory ware in a row.But not the results as expected.
You can help me get the results as below? With column Inventory
ID Items Import Export Inventory
1 A1 1333 0 1333
2 A1 0 368 965
3 A1 0 252 713
4 A1 1965 0 2678
5 A1 0 162 2516
6 A1 0 551 1965
7 A1 0 69 1896
This is my code:
Select ID,
(A.Invent + Import-Sum(Export)) as Inventory
From MyColumn,
(
Select Top 1 (Import - Export) as Invent
From MyColumn
Where Items in ('A1')
) as A
Where Items in ('A1')
Group by
A.Invent,
Import,ID
Being 2008 you're missing the sum() over, however there is another option
Example
Declare #YourTable Table ([ID] int,[Items] varchar(50),[Import] int,[Export] int)
Insert Into #YourTable Values
(1,'A1',1333,0)
,(2,'A1',0,368)
,(3,'A1',0,252)
,(4,'A1',1965,0)
,(5,'A1',0,162)
,(6,'A1',0,551)
,(7,'A1',0,69)
Select A.*
,B.*
from #YourTable A
Cross Apply (
Select Inventory = sum(Import-Export)
From #YourTable
Where Items=A.Items and ID<=A.ID
) B
Returns
ID Items Import Export Inventory
1 A1 1333 0 1333
2 A1 0 368 965
3 A1 0 252 713
4 A1 1965 0 2678
5 A1 0 162 2516
6 A1 0 551 1965
7 A1 0 69 1896
Using a join
Also window function but this is not available in 2008
declare #T table (id int identity primary key, import int, export int);
insert into #T (import, export) values
(1333, 0)
, (0, 368)
, (0, 252)
, (1965, 0)
, (0, 162)
, (0, 551)
, (0 , 69);
select t1.id, t1.import, t1.export
, sum(t2.import - t2.export) AS inven
from #T t1
join #T t2
on t2.id <= t1.id
group by t1.id, t1.import, t1.export
order by t1.id
select *
, sum(import - export) over (order by t.id) as inven
from #T t;
id import export inven
----------- ----------- ----------- -----------
1 1333 0 1333
2 0 368 965
3 0 252 713
4 1965 0 2678
5 0 162 2516
6 0 551 1965
7 0 69 1896
I have a query that returns something similar to the following:
Zone | NeededItems
===========================
209 | 5
213 | 1
216 | 1
220 | 2
218 | 1
219 | 4
215 | 1
The query behind it is something like:
SELECT
r.Zone as Zone, r.Required - COUNT(i.Item) as NeededItems
FROM
MyItems i
INNER JOIN
MyRequirements r ON i.Zone = r.Zone
GROUP BY
r.Zone, r.Required
Where MyItems looks like: (Value of Item doesn't matter)
Zone | Item
================
209 | a
209 | b
209 | c
216 | a
220 | a
213 | z
218 | x
219 | q
219 | w
219 | e
219 | r
215 | t
And MyRequirements looks like:
Zone | Required
======================
209 | 8
213 | 2
216 | 2
220 | 3
218 | 2
219 | 5
215 | 2
What I need to be able to do is print out the Zone multiple times based on the value in Needed. The value in Needed is a calculated value which is what is making this difficult (I can't just remove the count!)
So the results I am looking for is simply a list of zones, each appearing the number of times it is needed.
Zone
====
209
209
209
209
209
213
216
220
220
218
219
219
219
219
215
Is there any way in SQL that this can be done? Using SQL Server 2012.
Below is one way to do it - using the e1, e2 and e3 queries are not the cleanest way to do it, but it's the only way that I could manage to get it working.
One bit limitation: it only works for up to 1000 items of each (more than enough for mine.) This could be changed by editing WHERE c<9 but be aware this is recursive so best not to have it more than what is needed.
WITH CTE as
(
SELECT
r.Zone as Zone, r.Required - COUNT(i.Item) as NeededItems
FROM
MyItems i
INNER JOIN
MyRequirements r ON i.Zone = r.Zone
GROUP BY
r.Zone, r.Required
),
e1(n,c ) AS
(
SELECT 1, 0
UNION ALL
SELECT n, c + 1
FROM e1
WHERE c<9
), -- 10
e2(n) AS
(
SELECT 1 FROM e1 CROSS JOIN e1 AS b -- 100
),
e3(n) AS
(
SELECT 1 FROM e1 CROSS JOIN e2 -- 1000
),
Numbers AS
(
SELECT n = ROW_NUMBER() OVER (ORDER BY n) FROM e3
)
SELECT
Zone
FROM
Numbers
INNER JOIN
CTE on CTE.NeededItems >= n ORDER BY Zone
I have tables
table1
epid etid id EValue reqdate
----------- ----------- ----------- ------------ ----------
15 1 1 498925307069 2012-01-01
185 1 2 A5973FC43CE3 2012-04-04
186 1 2 44C6A4B776A2 2012-04-05
205 1 2 7A0ED3F1DA13 2012-09-19
206 1 2 77771D65F9C4 2012-09-19
207 1 2 AD74A4AA41BD 2012-09-19
208 1 2 9595ABE5A0C8 2012-09-19
209 1 2 7611D2FB395B 2012-09-19
210 1 2 04A510D6067A 2012-09-19
211 1 2 24D43EC268F8 2012-09-19
table2
PEId Id EPId
----------- ----------- -----------
43 9 15
44 10 15
45 11 15
46 12 15
47 13 15
48 14 15
49 15 15
50 16 15
51 17 15
52 18 15
table3
PLId PEId Id ToPayId
----------- ----------- ----------- -----------
71 43 9 1
72 43 9 2
73 44 10 1
74 44 10 2
75 45 11 1
76 45 11 2
77 46 12 1
78 46 12 2
79 47 13 1
80 47 13 2
I want to get one id whose count is less than 8 in table 3 and order by peid in table 2,
I have written query
SELECT Top 1 ToPayId FROM
(
SELECT Count(pl.ToPayId) C, pl.ToPayId
FROM table3 pl
INNER JOIN table2 pe ON pl.peid = pe.peid
INNER JOIN table1 e ON pe.epid = e.epid
WHERE e.EtId=1 GROUP BY pl.ToPayId
) As T
INNER JOIN table2 p ON T.ToPayId= p.Id
WHERE C < 8 ORDER BY p.PEId ASC
This query executes more than 1000 times in stored procedure depends on the entries in user-defined-table-type using while condition.
But it is very slow as we have millions of entries in each table.
Can anyone suggest better query regarding above?
maybe try with the having clause to get rid of the from select
select table2.id as due
from table3 inner join table2 on table2.PEId=table3.PEId...
group by ...
having count(due) <8
order by ...
-> you have a redundant Id column in table3 : seems pretty useless as the couple PEId and Id appears unique so remove it and reduce the size of table 3 by 25% hence improving performance of db
Will.. since you did not provide enough sample data and I am not sure what exactly your business logic is. So that I can just modify the code in blind.
SELECT ToPayId
FROM (
SELECT TOP 1 Count(pl.ToPayId) C, pl.ToPayId, pe.PEId
FROM table3 as pl
INNER JOIN table2 as pe ON pl.peid = pe.peid AND pl.ToPayId = pe.Id
INNER JOIN table1 e ON pe.epid = e.epid
WHERE e.EtId=1
GROUP BY pl.ToPayId, pe.PEId
HAVING Count(pl.ToPayId) < 8
ORDER BY pe.PEId ASC
) AS T
I have a query which delivers data like
ID Value AttributeID IDParent
286 Fleet 9 284
286 239 10 284
286 1 12 284
208 Rivers 9 -1
208 319 10 -1
208 0 12 -1
This result is need to be refined. Is there a way the using "query of queries" it can be transformed like
ID Value PageID Show IDParent
286 Fleet 239 1 284
208 Rivers 319 0 -1
or what will be a better way to do this.
You can do it like this:
select t.ID,
t.Value,
aux1.Value as 'PageID'
aux2.Value as 'Show'
t.IDParent
from tablename t
inner join tablename aux1 on aux1.IDParent = t.IDParent and aux1.AttributeID = 10
inner join tablename aux2 on aux2.IDParent = t.IDParent and aux2.AttributeID = 12
where t.AttributeID = 9
I'm trying to use the update function in T-sql in a trigger to check if a column has been changed or not. This is what I'm doing:
declare column_name cursor for select name from sys.columns where object_id = object_id(N'tblKit')
open column_name
fetch column_name into #colname
while (##Fetch_status=0)
begin
if(Update(#colname))
I get an error saying non-boolean type specified where a condition is expected. This is the syntax used in the msdn forum. So is there anything wrong with this?
I'm using Microsoft SQL server 2008 with Management Studio
If you simply want to log changes of individual columns in your trigger, you could try unpivoting, possibly in conjunction with full join. The idea is, you unpivot both inserted and deleted then join them on the table's key and the column containing the unpivoted names, filtering out the rows where the values are same.
Here's an example illustration of the method.
First, the table definitions:
CREATE TABLE TestTable (
ID int IDENTITY PRIMARY KEY,
Attr1 int,
Attr2 int,
Attr3 int
);
CREATE TABLE TestTableLog (
ID int IDENTITY PRIMARY KEY,
TableID int,
AttrName sysname,
OldValue int,
NewValue int,
Timestamp datetime DEFAULT GETDATE()
);
Next, a trigger for logging changes. This one will catch all the operations: insert, update, and delete:
CREATE TRIGGER trTestTable ON TestTable
AFTER INSERT, UPDATE, DELETE
AS BEGIN
WITH inserted_unpivot AS (
SELECT
ID,
AttrName,
Value
FROM inserted i
UNPIVOT (Value FOR AttrName IN (Attr1, Attr2, Attr3)) u
),
deleted_unpivot AS (
SELECT
ID,
AttrName,
Value
FROM deleted d
UNPIVOT (Value FOR AttrName IN (Attr1, Attr2, Attr3)) u
)
INSERT INTO TestTableLog (TableID, AttrName, OldValue, NewValue)
SELECT
ISNULL(i.ID, d.ID),
ISNULL(i.AttrName, d.AttrName),
d.Value,
i.Value
FROM inserted_unpivot i
FULL JOIN deleted_unpivot d
ON i.ID = d.ID AND i.AttrName = d.AttrName
WHERE CASE i.Value WHEN d.Value THEN 0 ELSE 1 END = 1
END
Now let's fill the TestTable with some data:
WHILE (SELECT COUNT(*) FROM TestTable) < 15
INSERT INTO TestTable
SELECT RAND() * 1000, RAND() * 1000, RAND() * 1000
;
Here's its contents before the subsequent changes:
ID Attr1 Attr2 Attr3
----------- ----------- ----------- -----------
1 820 338 831
2 795 881 453
3 228 430 719
4 36 236 105
5 246 115 649
6 488 657 438
7 990 360 15
8 668 978 724
9 872 385 562
10 460 396 462
11 62 599 630
12 145 815 439
13 595 7 54
14 587 85 655
15 80 606 407
And now let's perform some modifications to the contents:
UPDATE TestTable SET Attr2 = 35 WHERE ID = 3;
UPDATE TestTable SET Attr3 = 0 WHERE ID BETWEEN 6 AND 10;
INSERT INTO TestTable VALUES (1, 1, 1);
DELETE FROM TestTable WHERE ID = 14;
Here's what we've got in TestTable afterwards:
ID Attr1 Attr2 Attr3
----------- ----------- ----------- -----------
1 820 338 831
2 795 881 453
3 228 35 719
4 36 236 105
5 246 115 649
6 488 657 0
7 990 360 0
8 668 978 0
9 872 385 0
10 460 396 0
11 62 599 630
12 145 815 439
13 595 7 54
15 80 606 407
16 1 1 1
And this is what has been logged:
ID TableID AttrName OldValue NewValue Timestamp
----------- ----------- ----------- ----------- ----------- -----------------------
1 3 Attr2 430 35 2011-08-22 20:12:19.217
2 10 Attr3 462 0 2011-08-22 20:12:19.227
3 9 Attr3 562 0 2011-08-22 20:12:19.227
4 8 Attr3 724 0 2011-08-22 20:12:19.227
5 7 Attr3 15 0 2011-08-22 20:12:19.227
6 6 Attr3 438 0 2011-08-22 20:12:19.227
7 16 Attr1 NULL 1 2011-08-22 20:12:19.227
8 16 Attr3 NULL 1 2011-08-22 20:12:19.227
9 16 Attr2 NULL 1 2011-08-22 20:12:19.227
10 14 Attr1 587 NULL 2011-08-22 20:12:19.230
11 14 Attr2 85 NULL 2011-08-22 20:12:19.230
12 14 Attr3 655 NULL 2011-08-22 20:12:19.230
The setup, of course, has been somewhat simplified. In particular, all the main table's columns that are to be logged are of the same type, and so there's no need to convert data to some generic type to embrace various kinds of data. But maybe that's just what you need. And if not, I believe this can provide a good start to implementing the ultimate solution.