Update record with previous row - sql-server

I have a situation where I need to update the records with previous row value.
Source:
|MatId | BaseId |Flag|Pkg1| CS1
--------------------------------
|3001 | 3001 | 1 | 20 | 2 |
|3002 | 3001 | 0 | 15 | 3 |
|3003 | 3001 | 0 | 10 | 4 |
Here both 3001 (MatID) and 3001(BaseID) are same so FLAG =1, in the next record only BASEID is same. The output should be only PKG1 field updated with the current row value.
Target or output:
|MatId | BaseId|Flag|Pkg1|CS1
------------------------------
|3001 | 3001 | 1 | 20 | 2|
|3002 | 3001 | 0 | 20 | 3|
|3003 | 3001 | 0 | 20 | 4|
As seen in the target above i have to update the two values in PKG1 with the value from first record 20. Also there are many columns with Pkg1, how to update all the columns with a single query?
Any help is very much appreciated.
Thanks.

To get Previous and Next value with the help of LEAD and LAG Function in SQL Server is very simple. If you are using an earlier version of SQL Server than 2012 which does not support LEAD and LAG function we can use ROW_NUMBER().
Try to use something like this:
;WITH t AS
(
select LAG(MatId) OVER (ORDER BY MatId) AS previousMatId
, BaseId
, MatId
from TABLE
)
update tab
set tab.Pkg1 = p.Pkg1
from TABLE tab
inner join t on tab.MatId = t.MatId and t.BaseId = t.previousMatId
left join (select MatId AS MatId
, ISNULL(LAG(Pkg1) OVER (ORDER BY MatId), Pkg1) AS Pkg1
from TABLE) p on t.MatId = p.MatId

Are you saying the newer mats need to be updated with the Pkg1 belonging to the original mat? If so it would be:
update NewMats
set NewMats.Pkg1 = Base.Pkg1
from MyTabe as NewMats
inner join (select BaseId, Pkg1
from MyTable
where BaseId = MatId) as Base
on Base.BaseId = NewMats.BaseId
where NewMats.BaseId < NewMats.MatId
But if this is the case, then your data model needs to be changed. The rule is that a given piece of information should live in only one place. So maybe break this out into 2 tables that are related.

Related

SQL Server find sum of values based on criteria within another table

I have a table consisting of ID, Year, Value
---------------------------------------
| ID | Year | Value |
---------------------------------------
| 1 | 2006 | 100 |
| 1 | 2007 | 200 |
| 1 | 2008 | 150 |
| 1 | 2009 | 250 |
| 2 | 2005 | 50 |
| 2 | 2006 | 75 |
| 2 | 2007 | 65 |
---------------------------------------
I then create a derived, aggregated table consisting of an ID, MinYear, and MaxYear
---------------------------------------
| ID | MinYear | MaxYear |
---------------------------------------
| 1 | 2006 | 2009 |
| 2 | 2005 | 2007 |
---------------------------------------
I then want to find the sum of Values between the MinYear and MaxYear foreach ID in the aggregated table, but I am having trouble determining a proper query.
The final table should look something like this
----------------------------------------------------
| ID | MinYear | MaxYear | SumVal |
----------------------------------------------------
| 1 | 2006 | 2009 | 700 |
| 2 | 2005 | 2007 | 190 |
----------------------------------------------------
Right now I can perform all the joins to create the second table. But then I use a fast forward cursor to iterate through each record of the second table with the code inside the for loop looking like the following
DECLARE #curMin int
DECLARE #curMax int
DECLARE #curID int
FETCH Next FROM fastCursor INTo #curISIN, #curMin , #curMax
WHILE ##FETCH_STATUS = 0
BEGIN
SELECT Sum(Value) FROM ValTable WHERE Year >= #curMin and Year <= #curMax and ID = #curID
Group By ID
FETCH Next FROM fastCursor INTo #curISIN, #curMin , #curMax
Having found the sum of values between specified years, I can connect it back to the second table and I wind up the desired result (the third table).
However, the second table in reality is roughly 4 million rows, so this iteration is extremely time consuming (~generating 300 results a minute) and presumably not the best solution.
My question is, is there a way to generate the third table's results without having to use a cursor/for loop?
During a group by the sum will only be for the ID in question -- since the min year and max year is for the ID itself then you don't need to double query. The query below should give you exactly what you need. If you have a different requirement let me know.
SELECT ID, MIN(YEAR) as MinYear, MAX(YEAR) as MaxYear, SUM(VALUE) as SUMVALUE
FROM tablenameyoudidnotsay
GROUP BY ID
You could use query as bellow
TableA is your first table, and TableB is the second one
SELECT *,
(select SUM(Value) FROM TableA where tablea.ID=TableB.ID AND tableA.Year BETWEEN
TableB.MinYear AND TableB.MaxYear) AS SumValue
from TableB
You can put your criteria into a join and obtain the result all as one set which should be faster:
SELECT b.Id, b.MinYear, b.MaxYear, sum(a.Value)
FROM Table2 b
JOIN Table1 a ON a.Id=b.Id AND b.MinYear <= a.Year AND b.MaxYear >= a.Year
GROUP BY b.Id, b.MinYear, b.MaxYear

Why is my update query not actually updating?

i have been trying out an update query with information from another table but somehow is not working and i can't figure out why it is
this is how i'm doing the query:
UPDATE x
SET x.sheet_expedient=db_data.sheet_expedient
FROM dbo_expedient_reports x
INNER JOIN db_data ON x.sheet_expedient= db_data.sheet_expedient
WHERE x.sheet_expedient IS NULL
why is this not working? thanks in advance.
EDIT
i see some people a little confused about the structure of the table, here it is
dbo_expedient_reports
report_id|sheet_expedient|report_number|report_date|notificacion_date|report_status|
1 | NULL | NULL | NULL | NULL | NULL |
2 | NULL | NULL | NULL | NULL | NULL |
3 | NULL | NULL | NULL | NULL | NULL |
4 | NULL | NULL | NULL | NULL | NULL |
5 | NULL | NULL | NULL | NULL | NULL |
6 | NULL | NULL | NULL | NULL | NULL |
7 | NULL | NULL | NULL | NULL | NULL |
8 | NULL | NULL | NULL | NULL | NULL |
db_data (this one has the information i want to put on dbo_expedient_reports)
sheet_expedient|report_date|name_expedient_owner|address_expedient|
1 | 01-01-2011|mike | his house 123 |
2 | 06-06-2006|josh | their house 456 |
3 | 07-07-2007|andrew | his place 789 |
4 | 08-08-2008|frank | somewhere 1111 |
5 | 09-09-2009|chad | anywhere 2222 |
6 | 10-10-2010|zack | nowhere 3333 |
7 | 11-11-2011|steve | everywhere 4444 |
8 | 12-12-2012|mark | here 5555 |
The main idea is that the sheet expedient information goes to dbo_expedient reports for an instance and the other rows from db_data go another tables where the information will be placed, you may think, the sheet expedient is the same as the id, well that is not the case since the sheet_expedient gets to a limit (around 800) and then starts over again so it's different from the id, while the sheet_expedient will hit the number 800 then will start again so the id will be id 800 sheet_expedient 800 and then id 801 sheet_expedient 1
i hope a clarified some doubts for a better understanding, thanks for all of the replies
Based on your comment, you have JOIN on the wrong column. Here is the fix.
UPDATE x
SET x.sheet_expedient=db_data.sheet_expedient
FROM dbo_expedient_reports x
INNER JOIN db_data ON x.report_id = db_data.sheet_expedient --here is the change
WHERE x.sheet_expedient IS NULL
Note, all this is really going to do is duplicate the report_id column. That is, the report_id column will match the sheet_expedient column for your dbo_expedient_reports table. I'm not sure what the point of this is.
Also, the WHERE clause isn't needed based on this sample data.
I'm not too sure what would you like to achieve.
If x.sheet_expedient is expect to be NULL then why did you use it in the join?
Do you have any other fields that you can use to join the two tables?
Think you need a left join:
UPDATE x
SET x.sheet_expedient=db_data.sheet_expedient
FROM dbo_expedient_reports x
LEFT JOIN db_data ON x.sheet_expedient= db_data.sheet_expedient
WHERE x.sheet_expedient IS NULL
To check try this:
WITH cte AS
(SELECT x.sheet_expedient [FieldToUpdate]
, db_data.sheet_expedient [NewValue]
, *
FROM dbo_expedient_reports x
INNER JOIN db_data ON x.sheet_expedient = db_data.sheet_expedient
WHERE x.sheet_expedient IS NULL)
--UPDATE cte SET [FieldToUpdate] = [NewValue];
SELECT * FROM cte;
I usually wrap my updates into a cte before running them. Allows me to see values before and values after. While this won't answer your question, it should help you identify why.
I've commented out the update statement, once the select is returning the rows and values you expect, uncomment the update, and comment out the select.

SQL Query to select one set when there are multiple entries

I asked this question (SQL Query to select one set when there are duplicates) last year and got the solution to count the SLAs. Basically, count the number of minimum SLA for each application. However, I have a follow-up question. I want a query that will return the rows of the minimum SLA and earliest date for each REF_ID (or APP_ID)
ID | REF_ID | APP_ID | FIRST_DATE | SECOND_DTE | SLA |
1 | 11 | 101 | 2016/10/01 | 2016/10/02 | 1 |
2 | 12 | 102 | 2016/10/01 | 2016/10/04 | 2 |
3 | 12 | 102 | 2016/10/01 | 2016/10/05 | 2 |
So the query should return the first and second row.
I would very much appreciate if someone could provide a solution.
I have updated the query based on User726720 answer. This does not return entire rows but sufficient data.
SELECT REF_ID, MIN(SECOND_DTE), MIN(SLA) FROM TABLE WHERE FIRST_DTE > '2016-10-01' AND FIRST_DTE < '2016-11-01' GROUP BY REF_ID
This should do the job:
Select * from table
WHERE SLA = ( SELECT MIN(SLA) FROM table)
and SECOND_DTE = ( SELECT MIN(SECOND_DTE ) FROM table)

Getting a lineage of linked rows with details

I'm trying to get a "lineage" or similar, and also information about the first and last links (at least; all would be good), out of a table that has self-referential links between rows that have been "replaced" and rows that have replaced them. The table has a structure along these lines:
CREATE TABLE Thing (
Id INT PRIMARY KEY,
TStamp DATETIME,
Replaces INT NULL,
ReplacedBy INT NULL
);
I'm stuck with this structure. :-) It's sort of doubly-linked (yes, it's a bit silly): Each row has a unique Id, and then a row that has been "replaced" by another will have a non-NULL ReplacedBy giving the Id of the replacement row, and the replacement row will also have a link back to what it replaces in Replaces. So we can use either Replaces or ReplacedBy (or both) if we like.
Here's some sample data:
INSERT INTO Thing
(Id, TStamp, Replaces, ReplacedBy)
VALUES
(1, '2017-01-01', NULL, 11),
(2, '2017-01-02', NULL, 12),
(3, '2017-01-03', NULL, NULL),
(4, '2017-01-04', NULL, NULL),
(11, '2017-01-11', 1, NULL),
(12, '2017-01-12', 2, 22),
(22, '2017-01-22', 12, NULL);
So 1 was replaced by 11, 2 was replaced by 12, and 12 was replaced by 22.
I'd like to get the following information for each chain of links from this table in a reasonable way:
Details of the row that started the chain
Details of the final row in the chain
Details of the links in-between or at least how many links (total) there are in the chain
...filtered by a date range applied to the last row in the chain.
In an ideal universe, I'd get back something like this:
+−−−−−−−−−+−−−−−−−−+−−−−+−−−−−−−+−−−−−−−−−−−−+
| FirstId | LastId | Id | Links | TStamp |
+−−−−−−−−−+−−−−−−−−+−−−−+−−−−−−−+−−−−−−−−−−−−+
| 1 | 11 | 1 | 2 | 2017−01−01 |
| 1 | 11 | 11 | 2 | 2017−01−11 |
| 2 | 22 | 2 | 3 | 2017−01−02 |
| 2 | 22 | 12 | 3 | 2017−01−12 |
| 2 | 22 | 22 | 3 | 2017−01−22 |
+−−−−−−−−−+−−−−−−−−+−−−−+−−−−−−−+−−−−−−−−−−−−+
So far I have this query, which I could post-process to get the above:
WITH Data AS (
SELECT Id, TStamp, Replaces, ReplacedBy, 0 AS Depth
FROM Thing
UNION ALL
SELECT Thing.Id, Thing.TStamp, Thing.Replaces, Thing.ReplacedBy, Depth + 1
FROM Data
JOIN Thing
ON Thing.Replaces = Data.Id
)
SELECT *
FROM Data
WHERE ReplacedBy IS NOT NULL OR Depth > 0
ORDER BY
Id, Depth;
That gives me:
+−−−−+−−−−−−−−−−−−+−−−−−−−−−−+−−−−−−−−−−−−+−−−−−−−+
| Id | TStamp | Replaces | ReplacedBy | Depth |
+−−−−+−−−−−−−−−−−−+−−−−−−−−−−+−−−−−−−−−−−−+−−−−−−−+
| 1 | 2017−01−01 | NULL | 11 | 0 |
| 2 | 2017−01−02 | NULL | 12 | 0 |
| 11 | 2017−01−11 | 1 | NULL | 1 |
| 12 | 2017−01−12 | 2 | 12 | 0 |
| 12 | 2017−01−12 | 2 | 12 | 1 |
| 22 | 2017−01−13 | 12 | NULL | 1 |
| 22 | 2017−01−13 | 12 | NULL | 2 |
+−−−−+−−−−−−−−−−−−+−−−−−−−−−−+−−−−−−−−−−−−+−−−−−−−+
And I could use something like this to figure out (for instance) the final row of each chain:
WITH Data AS (
SELECT Id, Replaces, ReplacedBy, 0 AS Depth
FROM Thing
UNION ALL
SELECT Thing.Id, Thing.Replaces, Thing.ReplacedBy, Depth + 1
FROM Data
JOIN Thing
ON Thing.Replaces = Data.Id
),
MaxData AS (
SELECT Data.Id, Data.Depth
FROM Data
JOIN (
SELECT Id, MAX(Depth) AS MaxDepth
FROM Data
GROUP BY Id
) j ON data.Id = j.Id AND Data.Depth = j.MaxDepth
WHERE Depth > 0
)
SELECT *
FROM MaxData
ORDER BY
Id;
...which gives me:
+−−−−+−−−−−−−+
| Id | Depth |
+−−−−+−−−−−−−+
| 11 | 1 |
| 12 | 1 |
| 22 | 2 |
+−−−−+−−−−−−−+
...but I've lost the starting point and the points along the way.
I have the strong feeling I'm missing something really straight-forward — but clever — that would let me get this largely with the query rather than post-processing, some kind of join with a "min" and "max" query (but not like my one above). What would it be?
The table doesn't have any indexes on Replaces or ReplacedBy, but we could add any needed. The table is only lightly used (roughly 300k rows and probably only a couple of hundred updates/inserts a day).
I'm limited to SQL Server 2008 features.
Inspired by Gordon Linoff's answer and HABO's comment which highlighted something Gordon was doing that was critical, I:
Removed the SQL Server 2012+ FIRST_VALUE function, replacing it with a CROSS JOIN on an "overview" query of the data
Included the Links count in the overview query
Removed the reliance on t in Gordon's WHERE NOT EXISTS (SELECT 1 FROM Thing t2 WHERE t2.ReplacedBy = t.id), which (at last on SQL Server 2008) wasn't bound to anything
Filtered out rows that weren't replaced
Below, I also add the date filtering mentioned in the question
...filtered by a date range applied to the last row in the chain.
...which Gordon didn't cover at all, and changes our approach, but only in terms of the arrow of time.
So, first, without the date criteria, sticking fairly close to Gordon's answer:
WITH Data AS (
SELECT Id AS FirstId, Id, TStamp, Replaces, ReplacedBy, 0 AS Depth
FROM Thing
WHERE Replaces IS NULL AND ReplacedBy IS NOT NULL
UNION ALL
SELECT d.FirstId, t.Id, t.TStamp, t.Replaces, t.ReplacedBy, d.Depth + 1
FROM Data d
JOIN Thing t ON t.Replaces = d.Id
),
Overview AS (
SELECT FirstId, MAX(Id) AS LastId, COUNT(*) AS Links
FROM Data
GROUP BY
FirstId
)
SELECT d.FirstId, o.LastId, d.Id, o.Links, d.Depth, d.TStamp
FROM Data d
CROSS APPLY (
SELECT LastId, Links
FROM Overview
WHERE FirstId = d.FirstId
) o
ORDER BY
d.FirstId, d.Depth
;
The critical parts of that are grabbing the seed Id as FirstId here:
SELECT Id AS FirstId, Id, TStamp, Replaces, ReplacedBy, 0 AS Depth
FROM Thing
WHERE Replaces IS NULL AND ReplacedBy IS NOT NULL
and then propagating it through the results of the recursive join:
SELECT d.FirstId, t.Id, t.TStamp, t.Replaces, t.ReplacedBy, d.Depth + 1
FROM Data d
JOIN Thing t ON t.Replaces = d.Id
Just adding that to my original query gives us most of what I wanted. Then we add a second query to get the LastId for each FirstId (Gordon did it as a FIRST_VALUE over a partition, but I can't do that in SQL Server 2008) and using an overview query also lets me grab the number of links. We cross-apply that on the basis of the FirstId value to get the overall results I wanted.
The query above returns the following for the sample data:
+−−−−−−−−−+−−−−−−−−+−−−−+−−−−−−−+−−−−−−−+−−−−−−−−−−−−+
| FirstId | LastId | Id | Links | Depth | TStamp |
+−−−−−−−−−+−−−−−−−−+−−−−+−−−−−−−+−−−−−−−+−−−−−−−−−−−−+
| 1 | 11 | 1 | 2 | 0 | 2017-01-01 |
| 1 | 11 | 11 | 2 | 1 | 2017-01-11 |
| 2 | 22 | 2 | 3 | 0 | 2017-01-02 |
| 2 | 22 | 12 | 3 | 1 | 2017-01-12 |
| 2 | 22 | 22 | 3 | 2 | 2017-01-13 |
+−−−−−−−−−+−−−−−−−−+−−−−+−−−−−−−+−−−−−−−+−−−−−−−−−−−−+
...e.g., exactly what I wanted, plus Depth if I want (so I know what order the intermediary links were in).
If we wanted to include rows that were never replaced, we'd just change
WHERE Replaces IS NULL AND ReplacedBy IS NOT NULL
to
WHERE Replaces IS NULL
Giving us:
+−−−−−−−−−+−−−−−−−−+−−−−+−−−−−−−+−−−−−−−+−−−−−−−−−−−−+
| FirstId | LastId | Id | Links | Depth | TStamp |
+−−−−−−−−−+−−−−−−−−+−−−−+−−−−−−−+−−−−−−−+−−−−−−−−−−−−+
| 1 | 11 | 1 | 2 | 0 | 2017-01-01 |
| 1 | 11 | 11 | 2 | 1 | 2017-01-11 |
| 2 | 22 | 2 | 3 | 0 | 2017-01-02 |
| 2 | 22 | 12 | 3 | 1 | 2017-01-12 |
| 2 | 22 | 22 | 3 | 2 | 2017-01-13 |
| 3 | 3 | 3 | 1 | 0 | 2017-01-03 |
| 4 | 4 | 4 | 1 | 0 | 2017-01-04 |
+−−−−−−−−−+−−−−−−−−+−−−−+−−−−−−−+−−−−−−−+−−−−−−−−−−−−+
But we've ignored the date criteria required by the question:
...filtered by a date range applied to the last row in the chain.
To do that without building a massive temporary result set, we have to work backward: Instead of selecting the starting point (the first entry in a chain, Replaces IS NULL), we need to select the ending point (the last entry in a chain, ReplacedBy IS NULL), and then invert our logic working back through the chain. It's largely a matter of:
Swapping FirstId with LastId
Swapping Replaces with ReplacedBy (convenient the table had both!)
Using MIN to get the first ID in the chain rather than MAX to get the last
Using d.Depth - 1 rather than d.Depth + 1
Then fixing-up Depth based on Links once we know it in our final select, to get those nice values where 0 = first link rather than some varying negative number: o.Links + d.Depth - 1 AS Depth
All of which gives us:
WITH Data AS (
SELECT Id AS LastId, Id, TStamp, Replaces, ReplacedBy, 0 AS Depth
FROM Thing
WHERE ReplacedBy IS NULL AND Replaces IS NOT NULL
-- Filtering by date of last entry would go here
UNION ALL
SELECT d.LastId, t.Id, t.TStamp, t.Replaces, t.ReplacedBy, d.Depth - 1
FROM Data d
JOIN Thing t ON t.ReplacedBy = d.Id
),
Overview AS (
SELECT LastId, MIN(Id) AS FirstId, COUNT(*) AS Links
FROM Data
GROUP BY
LastId
)
SELECT o.FirstId, d.LastId, d.Id, o.Links, o.Links + d.Depth - 1 AS Depth, d.TStamp
FROM Data d
CROSS APPLY (
SELECT FirstId, Links
FROM Overview
WHERE LastId = d.LastId
) o
ORDER BY
o.FirstId, d.Depth
;
So for instance, if we used
AND TStamp BETWEEN '2017-01-12' AND '2017-02-01'
where I have
-- Filtering by date of last entry would go here
above, with our sample data we'd get this result:
+−−−−−−−−−+−−−−−−−−+−−−−+−−−−−−−+−−−−−−−+−−−−−−−−−−−−+
| FirstId | LastId | Id | Links | Depth | TStamp |
+−−−−−−−−−+−−−−−−−−+−−−−+−−−−−−−+−−−−−−−+−−−−−−−−−−−−+
| 2 | 22 | 2 | 3 | 0 | 2017−01−02 |
| 2 | 22 | 12 | 3 | 1 | 2017−01−12 |
| 2 | 22 | 22 | 3 | 2 | 2017−01−13 |
+−−−−−−−−−+−−−−−−−−+−−−−+−−−−−−−+−−−−−−−+−−−−−−−−−−−−+
...because the last link the Id = 1 chain is outside the date range, so we don't include it.
This is a little tricky. Arrange the CTE to start at the beginning of each list. That makes the subsequent processing easier:
WITH Data AS (
SELECT Id as FirstId, Id, TStamp, Replaces, ReplacedBy, 0 AS Depth
FROM Thing t
WHERE NOT EXISTS (SELECT 1 FROM Thing t2 WHERE t2.ReplacedBy = t.id)
UNION ALL
SELECT d.FirstId, t.Id, t.TStamp, t.Replaces, t.ReplacedBy, d.Depth + 1
FROM Data d JOIN
Thing t
ON t.Replaces = d.Id
)
SELECT d.*,
FIRST_VALUE(id) OVER (PARTITION BY FirstId ORDER BY Depth DESC) as LastId
FROM Data d;
Then, you can use FIRST_VALUE() with a reverse sort to get the last value in the chain.
This returns chains that have no links. You can add a filter to remove these.

Sorting items from same table

Hi i've got this little issue while sorting items within our database.
table data is like this:
id | description | parent | rowlevel
100 | item 12222 | -none- | 0
SET | item 12345 | -none- | 0
201 | item 22345 | -SET - | 1
I'd like have to output sorted on "id" though also have it sorted on a way that the "childen" come after the "parents".
I know this would be easier with a different table layout though changing that is not an option.
planned result would be:
id | description | parent | rowlevel
100 | item 12222 | -none- | 0
SET | item 12345 | -none- | 0
201 | item 22345 | -SET - | 1
301 | item 22345 | -SET - | 1
401 | item 22345 | -SET - | 1
ST2 | item 12345 | -none- | 0
211 | item 22345 | -ST2 - | 1
321 | item 22345 | -ST2 - | 1
101 | item 22345 | -ST2 - | 1
i've tried using order by but though its result has the rowlevel 1 items together the item's parent results at the bottom. together with the other parents.
I am not experiences using joins and have no clue if its posible to have a join on the table itself which will resolve this.
the only think I can think of is some sort of nested SQL query.
but besides that I am not sure if that will work. I am also concerned this will eat resources and have a great impact on the performance.
As long es you have only 1 level, just add the following order criterion to your SQL:
ORDER BY COALESCE(parent, id), rowlevel
(Assuming that -none- is actually null and SET/-SET - is actually some kind of numeric id.)
If you have more than 1 level of nesting, you will require some kind of recursive CTE.
You can use a recursive CTE:
WITH CTE AS
(
SELECT t1.[id], t1.[description], t1.[parent],
[rowlevel] = 0
FROM dbo.Table1 t1
WHERE t1.parent = '-none-'
UNION ALL
SELECT t2.[id], t2.[description], t2.[parent],
[rowlevel] = CTE.[rowlevel] + 1
FROM dbo.Table1 t2 INNER JOIN CTE
ON t2.parent = CTE.id
)
SELECT * FROM CTE
ORDER BY rowlevel, id
DEMO

Resources