I have 1 table, which refers by value to rows in the same table
Example table:
ID PARENT_ID NAME
1 0 john
2 1 jane
3 2 smigy
4 2 gujo
5 1 duby
6 1 ruby
7 5 foo
8 2 bar
9 3 baz
10 3 qux
The root-parent has parent 0 (just so it wouldn't be null), in this case there's
1 root-parent - parent(0)=1.
root-parent has 1lvl children - parent(1)=2;5;6.
1lvl children has 2lvl children - parent(2)=3;4;8. parent(5)=7. parent(6) has nothing.
2lvl children has 3lvl children - parent(3)=9;10. parent(4) has nothing. parent(8) has nothing.
There is no lvl4 children or anything with depth beyond 4.
And I need to create a script (presumably SQL query - need to avoid function/procedure/etc.) that would index rows based on their position under their parent.
Just like if I'd select all root-parent's and get (rownum-1)
The goal table should look like this:
ID PARENT_ID NAME ROW_INDEX
1 0 john 0
2 1 jane 0
3 2 smigy 0
4 2 gujo 1
5 1 duby 1
6 1 ruby 2
7 5 foo 0
8 2 bar 2
9 3 baz 0
10 3 qux 1
I'm planing to add this column and thus the query will be executed only once. I've played by selecting seperate depth rows, but then I don't really know how to count inside/between group by (even if that is possible).
P.S. A better/good column name suggestion would also be very appreciated.
User row_number()
select mt.*, row_number() over(partition by parent_id order by id) - 1 as rn
from MyTable mt
Related
I have such tables:
Group - combination of TypeId and ZoneId
ID TypeID ZoneID
-- -- --
1 1 1
2 1 2
3 2 1
4 2 2
5 2 3
6 3 3
Object
ID GroupId
-- --
1 1
2 1
3 2
4 3
5 3
6 3
I want to build a query for grouping all these tables by TypeId and ZoneId, with number of objects which have specific combination of these field:
ResultTable
TypeId ZoneId Number of objects
-- -- --
1 1 2
1 2 1
2 1 3
2 2 1
2 3 0
3 3 0
Query for this:
SELECT
group.TypeId,
group.ZoneId,
COUNT(obj.ID) as NumberOfObjects
FROM[Group] group
JOIN[Object] obj on obj.GroupID = group.ID
GROUP BY group.TypeId, group.ZoneId ORDER BY group.TypeId
But! I want to add summarize row after each group, and make it like:
ResultTableWithSummary
TypeId ZoneId Number of objects
-- -- --
1 1 2
1 2 1
Summary (empty field) 3
2 1 3
2 2 1
2 3 0
Summary (empty field) 4
3 3 0
Summary (empty field) 0
The problem is that I can use GROUP BY ROLLUP(group.TypeId, group.ZoneId):
TypeId ZoneId Number of objects
-- -- --
1 1 2
1 2 1
1 null 3
2 1 3
2 2 1
2 3 0
2 null 4
3 3 0
3 null 0
but I cannot or don't know how to change not-null group.TypeId in summary rows with "Summary".
How can I do this?
The simplest method is coalesce(), but you need to be sure the types match:
SELECT COALESCE(CONVERT(VARCHAR(255), group.TypeId, 'Summary') as TypeId,
. . .
This is not the most general method, because it does not handle real NULL values in the GROUP BY keys. That doesn't seem to be an issue in this case. If it were, you could use a CASE expression with GROUPING().
EDIT:
For your particular variant (which I find strange), you can use:
SELECT (CASE WHEN group.TypeId IS NULL OR group.ZoneID IS NULL
THEN 'Summary' ELSE CONVERT(VARCHAR(255), group.TypeId)
END) as TypeId,
. . .
In practice, I would use something similar to the COALESCE() in both columns, so I don't lose the information on what the summary is for.
I need to be able to create a column that has a count up to 3 but will provide numbers 1-3 three times each before starting over again.
My current code below only counts up to 3 and starts over. I need it to count each number 3 times then start over
Select personid, taskid, 1 + ( (row_number() over (order by personid) - 1) % 3) AS taskNumber2 from taskTable
Table in DB:
Personid taskid
1 1
1 2
2 3
2 4
2 5
3 6
4 7
4 8
5 9
5 10
5 11
Expected Results:
Personid taskid numberCount
1 1 1
1 2 1
2 3 1
2 4 2
2 5 2
3 6 2
4 7 3
4 8 3
5 9 3
5 10 1
5 11 1
You can use row_number to determine your value. Let the row number be x. Since you are repeating ervery 9th entry, take modulo 9. For easier calculating, subtract 1 before this. So now 0-2 -> 1, 3-5 -> 2, 6-8 -> 3. Thus devide by 3 (integer division) and add 1.
SELECT personid, taskid,
((row_number() OVER (ORDER BY personid) - 1) % 9) / 3 + 1 AS taskNumber2
FROM taskTable
ORDER BY personid
Also note, that I added the same ORDER BY clause to the whole statement as in the window function, thus assuring correct ordering (at least if this ordering is not ambigious, e.g. it is unique, perhaps a primary key)
I have a table of list items. There is a ListID column used as an identifier to group the list items together. Is there a sane way to give every item a sort order, starting at 0 per list and incremental by one per item.
Basically, I need to populate the following SortOrder Column values for a large number of entries/ListIDs.
ID ListID SortOrder
1 1 0
2 0 0
3 1 1
4 0 1
5 1 2
6 0 2
7 2 0
8 2 1
9 2 2
You can use ROW_NUMBER() with a PARTITION on the ListId field for this:
Select Id, ListId,
Row_Number() Over (Partition By ListId Order By Id) -1 As SortOrder
From YourTable
Order By Id
I think you want:
WITH toupdate as (
SELECT t.*,
ROW_NUMBER() OVER (PARTITION BY ListId Order By id) as new_SortOrder
FROM tableName
)
UPDATE toupdate a
SET sortorder = new_sort_order;
SQL Server has the nice ability to update a subquery or CTE under some circumstances.
Do you need to persist the order of lists containing items that are shared between lists? If so, perhaps variations on this schema would work for you.
Item
id label
1 A
2 B
3 C
4 D
List
id listName
1 abc list
2 cbd list
3 aaa list
ListMembership
id listId itemId order
1 1 1 1
2 1 2 2
3 1 3 3
4 2 2 2
5 2 3 1
6 2 4 3
7 3 1 1
8 3 1 2
9 3 1 3
usage:
select i.label from listMembership as lm
join Item as i on i.id=lm.itemId
where lm.listId=2
order by lm.order
yields:
label
C
B
D
I'm trying to create an SQL statement to match either an id number or a postcode and then assign a new id number
What I want to end up with is ‘newid’ that correctly recognizes that the first four records are the same person (even though the postcode for record 2 is different).
record id postcode newid
--------------------------
1 1 1 1
2 1 2 1
3 1 1 1
4 2 1 1
5 3 3 2
Any suggestions would be appreciated greatly.
Going based on your example:
SELECT RECORD,
(SELECT MIN (ID)
FROM users u2
WHERE users.id IN (u2.id, u2.postcode)
OR users.postcode in (u2.id, u2.postcode)
) AS newid
FROM users
This results with the following data:
RECORD NEWID
------------------
1 1
2 1
3 1
4 1
5 3
Here is the SQLFiddle
The simple question is how can you take a set of records with a PK and create exactly two records for each source with a slightly altered key for the duplicate? In other words, I take 4000 records and produce 8000 records where 4000 are identical and the other 4000 have a slightly altered key. I cannot do a union because this is essentially two selects (long story).
The rest gets complicated, but maybe necessary to provide examples.
This is my original set (it contains over 4000 records)
dateGroup areaGroup itemID editionID
1 1 1 1
1 1 1 2
1 2 1 1
1 2 2 1
2 1 1 1
2 1 1 2
2 2 1 1
2 2 1 2
For each record I need to create a duplicate record ganging the areaGroups together under '0', then create a comma separated list of original areaGroups as a separate field. (The "why" is some dumb programmer (me) made a mistake about 15 years ago.) I can renumber the editionIDs as necessary, but the original and duplicate record must have the same editionID (thus why a union wouldn't work). The PK remains the same as above (all fields)
dateGroup areaGroup itemID editionID aGroups
1 0 1 1 1
1 0 1 2 1
1 0 1 1 2 // Duplicate (EditionID)
1 0 2 1 2
2 0 1 1 1
2 0 1 2 1
2 0 1 1 2 // Duplicate (EditionID)
2 0 1 2 2
The end result would renumber the editionID as needed to make the record unique.
dateGroup areaGroup itemID editionID aGroups (EditionID is what is altered)
1 0 1 1 1
1 0 1 2 1
1 0 1 2 2 1 changed to 2 (one more than row 1)
1 0 2 1 2
2 0 1 1 1
2 0 1 2 1
2 0 1 2 2 1 changed to 2 (one more than row 1)
2 0 1 2 2
1 1 1 1
1 1 1 2
1 2 1 2 1 changed to 2 (editionID) to match
1 2 2 1
2 1 1 1
2 1 1 2
2 2 1 2 1 changed to 2 to match above
2 2 1 2
I know you could calculate the editionID like a row rank like so:
select row_number() over (
partition by dateGroup, itemID
order by dateGroup, itemID) as editionID
So all I need is to know how to duplicate the records from a single set
do a cross join on a derived table:
( select 1 as aGroups union all select 2 )
I'd create a temporary table with duplicates and their count.
Then I'd filter the original table to have only unique rows, and insert another row for each row in the temporary table, incrementing their editionID.
In MySQL, I'd use user #variables; not sure about MS SQL.
Did you try UNION ALL instead of just UNION
UDPATE perhaps I misunderstood the problem and I thought you were having a problem with the union loosing the duplicates.
If the problem is that you want to do a row_number over a union why don't you do somthing like
select row_number() over (
partition by dateGroup, itemID
order by dateGroup, itemID) as editionID
FROM
(
SELECT
dateGroup, itemID
FROM TableA
UNION ALL
SELECT
dateGroup, itemID
FROM TableB
) Data