SQL query to get multihierarchy items - sql

in my SQL Table i have following data
ID Level Description Code MasterID
1 1 Cars AD0 NULL
2 1 Trucks JF1 NULL
3 1 Items YU2 NULL
4 2 New Cars AS3 1
5 2 Used Cars TG4 1
6 2 Car parts UJ5 1
7 2 New trucks OL6 2
8 2 Used trucks PL7 2
9 2 Truck parts KJL8 2
10 2 Factory stuff UY9 3
11 2 Custom stuff RT10 3
12 3 Toyota 6YH11 4
13 3 BMW 9OKH12 4
14 3 VW 13 5
15 3 Tiers Type I J14 6
16 3 Tiers Type II J15 6
17 3 Tiers Type III ADS16 9
18 3 Seats SA17 6
19 3 Doors UU18 6
20 3 Lights 9OL19 6
21 4 Left light GH20 20
22 4 Right light H21 20
23 4 Left door JHJ22 19
24 4 Michelin UY23 16
25 4 Vredestein GTF24 17
26 4 Dunlop 25 15
My achievement is to get all hierarchy data for each single item. For Exmaple, the outpu should look like as following
ID Level Description Code MasterId1 Description1 MasterId2 Description2 MasterId3 Description3
24 4 Michelin UY23 16 Tiers Type II 6 Car Parts 1 Cars
.
.
19 3 Doors UU18 6 Car Parts 1 Cars NULL NULL
.
.
10 2 Factory Stuff UY9 3 Items NULL NULL NULL NULL
.
.
3 1 Items NULL NULL NULL NULL NULL NULL NULL
.
.
If somebody can help or give an advise how to achieve this?

This is not dynamic but it could be pretty easily.
Using a recursive cte you can get the hierarchy for the entire table and self join a few times to get the table structure you want.
;WITH cte AS
(
SELECT *, ID AS [RootID], 1 AS [MasterLevel] FROM Table1
UNION ALL
SELECT t1.*, cte.[RootID], cte.[MasterLevel] + 1 FROM Table1 t1
JOIN cte ON t1.ID = cte.MasterID
)
SELECT r.ID, r.[Level], r.[Description], r.[Code],
m1.ID AS MasterId1, m1.[Description] AS Description1,
m2.ID AS MasterId2, m1.[Description] AS Description2,
m3.ID AS MasterId3, m1.[Description] AS Description3
FROM cte r
LEFT JOIN cte m1 ON m1.[RootID] = r.[RootID] AND m1.MasterLevel = 2
LEFT JOIN cte m2 ON m2.[RootID] = r.[RootID] AND m2.MasterLevel = 3
LEFT JOIN cte m3 ON m3.[RootID] = r.[RootID] AND m3.MasterLevel = 4
WHERE r.MasterLevel = 1
ORDER BY r.RootID DESC, r.MasterLevel
This would build a dynamic sql to get master and desciption fields based on the maximum Level value. or you could define how many levels you want to see by changing the #MaxLevel
DECLARE #Sql VARCHAR(MAX) = '',
#SelectSql VARCHAR(MAX) = '',
#JoinSql VARCHAR(MAX) = '',
#MaxLevel INT,
#idx INT = 1
SET #MaxLevel = (SELECT MAX([Level]) FROM Table1)
WHILE #idx < #MaxLevel
BEGIN
SET #SelectSql = #SelectSql + REPLACE(', m<index>.ID AS MasterId<index>, m<index>.[Description] AS Description<index> ', '<index>', #idx)
SET #JoinSql = #JoinSql + REPLACE(' LEFT JOIN cte m<index> ON m<index>.[RootID] = r.[RootID] AND m<index>.MasterLevel = <index> ', '<index>', #idx)
SET #idx = #idx + 1
END
SET #Sql = '
;WITH cte AS
(
SELECT *, ID AS [RootID], 0 AS [MasterLevel] FROM Table1
UNION ALL
SELECT t1.*, cte.[RootID], cte.[MasterLevel] + 1 FROM Table1 t1
JOIN cte ON t1.ID = cte.MasterID
)
SELECT r.ID, r.[Level], r.[Description], r.[Code]' + #SelectSql
+ 'FROM cte r ' + #JoinSql
+ 'WHERE r.MasterLevel = 0
ORDER BY r.RootID DESC, r.MasterLevel'
EXEC(#Sql)

Related

Get datetime difference group by query results in one row

I have a table Master_History with structure as
Id_History Created_Date Subscription_Type rn
21 1/22/2016 16:31:29 1 1
22 1/22/2016 16:33:11 2 2
23 1/22/2016 16:33:37 1 3
24 1/22/2016 16:33:46 2 4
25 1/22/2016 16:33:53 1 5
26 1/22/2016 16:33:57 3 6
27 1/22/2016 16:34:01 2 7
28 1/22/2016 16:34:04 1 8
29 1/22/2016 16:34:08 3 9
I want to calculate date difference with adjacent rows which i have succeeded in calculating but results are getting distributed over multiple rows
Standard Plus Premium
122 NULL NULL
NULL 35 NULL
NULL NULL 3
I need
Results in one row like
Standard Plus Premium
122 35 3
For the last row (in this Subscription_Type is 3, the date difference should also get calculated on getdate() i.e. whenever i executed my query, seconds in Premium column should get reflected each time
Query :
WITH CTE
AS (
SELECT *
,ROW_NUMBER() OVER (
ORDER BY Created_Date
) AS rn
FROM Master_History
WHERE Client_ID = 11072
)
SELECT CASE
WHEN mc.Subscription_Type = 1
THEN Sum(DATEDIFF(second, mc.Created_Date, mp.Created_Date))
END AS [Standard]
,CASE
WHEN mc.Subscription_Type = 2
THEN Sum(DATEDIFF(second, mc.Created_Date, mp.Created_Date))
END AS Plus
,CASE
WHEN mc.Subscription_Type = 3
THEN Sum(DATEDIFF(second, mc.Created_Date, mp.Created_Date))
END AS Premium
FROM CTE mc
JOIN CTE mp ON mc.rn = mp.rn - 1
GROUP BY mc.Subscription_Type
try this
select
count(Standard.*) Standard_,
count(Plus.*) Plus_,
count(Premium.*) Premium_
from
Master_History master_
left outer join Master_History Standard on Standard.Subscription_Type = 1
and master_.Subscription_Type = Standard.Subscription_Type
left outer join Master_History Plus on Plus.Subscription_Type = 2
and master_.Subscription_Type = Plus.Subscription_Type
left outer join Master_History Premium on Premium.Subscription_Type = 3
and master_.Subscription_Type = Plus.Subscription_Type
where
convert(date,master_.Created_Date) < convert(date,getdate()) and
convert(date,master_.Created_Date) < convert(date,Standard.Created_Date) and
convert(date,master_.Created_Date) < convert(date,Plus.Created_Date) and
convert(date,master_.Created_Date) < convert(date,Premium.Created_Date)

sqlquery to get missing

I have a table student with following columns
StudentId SemesterId ExamYearId
1 1 1
1 2 2
1 3 3
3 1 1
3 2 2
3 3 4
7 1 1
7 3 4
8 1 1
8 2 2
I want a query to get all such student for whom no data exists for semesterid=3 and examyearid=3, however same student should have data for semesterid=2.
In this case it should return me studentid=8 as studentid=8 has data for semesterid=2 and examyearid=2 however no data for examyearid=3 and semesterid=3.
Basically I want to be able to input semesterid and examyearid and find which studentids have not filled data for that semesterid and examyearid.
DECLARE #SemesterId INT
,#ExamYearId INT
SET #SemesterId = 3;
SET #ExamYearId = 3;
SELECT *
FROM TableName t
WHERE EXISTS (SELECT 1
FROM TableName
WHERE SemesterId = #SemesterId - 1
AND ExamYearId = #ExamYearId - 1
AND t.StudentId = StudentId )
AND NOT EXISTS (SELECT 1
FROM TableName
WHERE SemesterId = #SemesterId
AND ExamYearId = #ExamYearId
AND t.StudentId = StudentId )
SELECT st1.*
FROM Student st1
left join Student st2
on st1.id = st2.id
and st1.Semesterid=2
and st2.Semesterid=3
and st2.Examyearid=3
where st2.id is null

SQL - Max value for item from different table per group

I have the following SQL tables, with the following data:
site-obj-Prices:
id Parameter Value ActionFunc ActionValue ChainTo ChainOperator GroupID
1 Locality 0 Set 6 NULL NULL 1
2 Locality 1 Set 3 NULL NULL 2
3 Locality 0 Set 15 4 AND 3
4 State 61 Set 15 NULL NULL 3
5 Locality 0 Set 18 6 AND 4
6 State 61 Set 18 7 AND 4
7 AreaCode 954 Set 18 NULL NULL 4
8 Locality 0 Add -1 9 AND 5
9 State 61 Add -1 10 AND 5
10 AreaCode 954 Add -1 11 AND 5
11 Supplier 242 Add -1 NULL NULL 5
12 Weight 3 Add 3 NULL NULL 6
13 Weight 3 Add 2 14 AND 7
14 Supplier 242 Add 2 NULL NULL 7
site-obj-PricesParams:
id Parameter Order
1 Locality 0
2 State 1
3 AreaCode 2
4 Weight 3
5 Supplier 4
And in the following query I need to change ActionLevel so it will reflect
MAX(Order) from [site-obj-PricesParams]
for the all the parameters in each GroupID.
So if I have a group with parameters 'Locality' and 'Weight', ActionLevel should be 3.
Any help will be appreciated.
SELECT
id, Parameter, Value, ActionFunc, ActionValue, ChainTo, ChainOperator, GroupID,
COUNT(GroupID) OVER(PARTITION BY GroupID) AS ActionLevel
FROM
[site-obj-Prices] as Actions
WHERE
GroupID NOT IN (SELECT [GroupID]
FROM [site-obj-Prices] as act
INNER JOIN #ParametersList as par ON act.Parameter = par.sKey
AND act.Value <> par.sValue
UNION
SELECT [GroupID]
FROM [site-obj-Prices] as act
LEFT JOIN #ParametersList as par ON act.Parameter = par.sKey
WHERE par.sKey IS NULL
)
ORDER BY
ActionLevel ASC
If i am not wrong in understanding your requirement then hopefully this would work.
SELECT id,Parameter,Value,ActionFunc,ActionValue,ChainTo,
ChainOperator,GroupID, MAX([Order]) OVER(PARTITION BY GroupID) AS ActionLevel
FROM [site-obj-Prices] as Actions
Inner Join [site-obj-PricesParams] as Params
On Actions.Parameter = Params.Parameter
Where GroupID NOT IN (
SELECT [GroupID]
FROM [site-obj-Prices] as act
INNER JOIN #ParametersList as par
ON act.Parameter=par.sKey AND act.Value<>par.sValue
UNION
SELECT [GroupID]
FROM [site-obj-Prices] as act
LEFT JOIN #ParametersList as par
ON act.Parameter=par.sKey WHERE par.sKey IS NULL
)
ORDER BY ActionLevel ASC

Select value where the value in the same row but another column is the same

I'm unable to write this query, please see the columns/data:
ID Type Regnr Samenr
1 Car 11 1
2 Plane 654 1
3 Bycicle 1311 1
4 Plane 232 2
5 Car 18 2
6 Bycicle 16 2
7 Motorcycle 13 2
For the [ID] number I receive I want to select the [Regnr] where the values are the same in column [Samenr] and WHERE [Type] = 'Plane'.
So if WHERE [ID] = 2, result will be 654. Or if WHERE [ID] = 1, I again get 654 as result. If WHERE [ID] = 7, the result should be 232.
You can use self-join
SELECT t2.regnr
FROM table1 t1 JOIN table1 t2
ON t1.samenr = t2.samenr
AND t2.type = 'Plane'
WHERE t1.id = ?
Here is SQLFiddle demo

MS-Access SQL loops

I would like to apply a for loop in this sql statement in ms-access something like:
for(i=0;i<8;i++)
{
UPDATE current_stock SET current_stock.quantity = DLookup("quantity","current_stock","stock_id=i")-DLookup("req_quantity","Bom_dell","lap_id=(SELECT lap_id FROM laptop_info WHERE model_name='Dell Inspiron')" And "stock_id=i")
WHERE stock_id=1;
}
Please advise if there is any method in MS-access to be able to do something similar to a for loop using sql queries.
Bom_dell
bom_id lap_id stock_id req_quantity
1 1 1 1
2 1 2 3
3 1 3 6
4 1 4 1
5 1 5 1
6 1 6 2
7 2 7 7
8 2 8 8
9 2 9 1
10 2 10 1
11 2 11 1
12 2 12 3
current_stock
ID lap_id stock_id quantity
1 1 1 11
2 1 2 11
3 1 3 11
lap_info
lap_id model_name model_num price
1 Dell Inspiron INS81 35000
2 Dell XLS XL91 24000
Sample query
UPDATE (SELECT laptop_info.model_name, current_stock.stock_id, Bom_dell.req_quantity, current_stock.quantity
FROM (current_stock INNER JOIN laptop_info ON current_stock.lap_id = laptop_info.lap_id) INNER JOIN Bom_dell ON current_stock.stock_id = Bom_dell.stock_id)
SET quantity=quantity-req_quantity
WHERE stock_id BETWEEN 1 AND 3
AND model_name='Dell Inspiron'
UPDATE (SELECT laptop_info.model_name, current_stock.stock_id, Bom_dell.req_quantity, current_stock.quantity
FROM (current_stock INNER JOIN laptop_info ON current_stock.lap_id = laptop_info.lap_id) INNER JOIN Bom_dell ON current_stock.stock_id = Bom_dell.stock_id) SET quantity = quantity-req_quantity
WHERE stock_id BETWEEN 1 AND 3
AND model_name IN ([Forms]![Invoice1]![laptop_id])
You seem to be looking for something on the lines of:
UPDATE (current_stock
INNER JOIN laptop_info ON laptop_info.stock_id=current_stock.stock_id)
INNER JOIN Bom_dell ON Bom_dell.lap_id = laptop_info.lap_id
SET current_stock.quantity = current_stock.quantity - Bom_dell.req_quantity
WHERE stock_id Between 1 And 8
AND model_name='Dell Inspiron'
Try this. I have used aliases - a is for the current_stock table and b is for the sub query. This assumes that you have one line per stock id in bom_dell.
UPDATE current_stock AS a
INNER JOIN (
SELECT bom_dell.stock_id, lap_info.model_name, bom_dell.req_quantity
FROM bom_dell
INNER JOIN lap_info ON bom_dell.lap_id = lap_info.lap_id
WHERE (((bom_dell.stock_id) Between 1 And 3)
AND ((lap_info.model_name)="Dell Inspiron"))) AS b
ON a.Stock_id = b.Stock_id SET a.quantity = [a].[quantity]+[b].[req_quantity];