How to add root second parent id to oracle table - sql

I have a child-parent table like this (0 means that it is the top item):
itemID | parentItemId
---------------------
1 | 0
2 | 0
3 | 0
4 | 1
5 | 1
6 | 2
7 | 5
8 | 7
9 | 7
I want to add a new column that will be rootItemId according to this logic:
If it is a top item then rootItemId = itemID, else rootItemId will be item under the root (i.e. level - 2) itemID.
It will look like this:
itemID | parentItemId | rootItemId
----------------------------------
1 | 0 | 1
2 | 0 | 2
3 | 0 | 3
4 | 1 | 4
5 | 1 | 5
6 | 2 | 6
7 | 5 | 5
8 | 7 | 5
9 | 7 | 5
I can get this result using the following sql:
select itemID,
parentItemId,
itemID as rootItemId
from ItemTable
where itemID = 0
union
select itemID,
parentItemId,
connect_by_root parentItemId as rootItemId
from ItemTable
start with parentItemId in
(select itemID
from ItemTable
where parentItemId =0)
connect by prior itemID = parentItemId
Is there a more elegant and efficient way to achieve the same results?
Thanks

Related

How to create column for every single integer within a range in SQLite?

Here's some sample data from my table:
day_number daily_users_count
1 1
3 1
6 1
7 1
9 2
10 2
I need all day_number values, from 1 to max(day_number), and I want daily_users_count to be zero if it isn't mentioned in this table.
It should look something like this:
day_number daily_users_count
1 1
2 0
3 1
4 0
5 0
6 1
7 1
8 0
9 2
10 2
I think a left join with a table which has a number column with all integers from 1 to max(day_number) would work, if I put a default value for daily_users_count as 0.
What I don't get is how to create such a table where all integers within a certain range are present. Any alternate solutions or any ways to do this would be much appreciated.
You can do it with a recursive CTE which will return all the day_numbers including the missing ones and then a LEFT join to the table:
with cte as (
select min(day_number) day_number from tablename
union all
select day_number + 1 from cte
where day_number < (select max(day_number) from tablename)
)
select c.day_number,
coalesce(t.daily_users_count, 0) daily_users_count
from cte c left join tablename t
on t.day_number = c.day_number
See the demo.
Results:
| day_number | daily_users_count |
| ---------- | ----------------- |
| 1 | 1 |
| 2 | 0 |
| 3 | 1 |
| 4 | 0 |
| 5 | 0 |
| 6 | 1 |
| 7 | 1 |
| 8 | 0 |
| 9 | 2 |
| 10 | 2 |

return true if the column which have the same ProductID

i have table name stock in which i have 5 column name id productId stockIn stockOut And VoucherType ..
i want to check the if productId=1 and voucherType=1 that count all the column in stockIn with the same ProductId and Sum All the value in stockIn then also count in stockout with same product id and sum if the stockIn sum is greater then stockOut that return true...
id ' productid ' stockin 'stockout 'VoucherType
-----------------------------------------
1 | 1 | 5 | 0 | 1
2 | 1 | 5 | 0 | 1
3 | 1 | 5 | 0 | 1
4 | 2 | 5 | 0 | 1
5 | 2 | 0 | 10 | 2
6 | 1 | 0 | 2 | 2
7 | 2 | 0 | 3 | 2
SELECT productId
, voucherType
, COUNT(stockIn) as Count_stockIn
, SUM(stockIn) as Sum_stockIn
, COUNT(stockOut) as Count_stockOut
, CASE WHEN SUM(stockIn) > SUM(stockOut) THEN 'True' ELSE 'False' END AS IsInGreater
From Table
Group By productId, voucherType
Having productId = voucherType And IsInGreater = 'True'
This is kind of what you want? (your question is seriously unclear)

Update a column and refer back it in the same query

I have a table in SQL Server 2014 and need to recursively update a column based on its previous value. For e.g.
---------------------------------------
ID | price | diff_with_prev_price |
---------------------------------------
1 | 29 | 0 |
2 | 25 | 0 |
3 | 20 | 0 |
4 | 35 | 0 |
5 | 40 | 0 |
--------------------------------------|
I want to recursively update third column like below
---------------------------------------
ID | price | diff_with_prev_price |
---------------------------------------
1 | 29 | 0 |
2 | 25 | 25 |
3 | 20 | 5 |
4 | 35 | -30 |
5 | 40 | 10 |
--------------------------------------|
It is the summation of previous value of third column with next value of 'price'.
Can someone please give some hint to do this either using CTE or LEAD/LAG, but without using cursors. I have to update million rows.
You can try this:
SELECT 1 AS ID , 29 AS price, 0 AS diff_with_prev_prive
INTO #tmp
UNION SELECT 2 AS ID , 25 AS price, 0 AS diff_with_prev_prive
UNION SELECT 3 AS ID , 20 AS price, 0 AS diff_with_prev_prive
UNION SELECT 4 AS ID , 35 AS price, 0 AS diff_with_prev_prive
UNION SELECT 5 AS ID , 40 AS price, 0 AS diff_with_prev_prive
WITH cte AS
(
SELECT
ID
, price
, diff_with_prev_prive
, price - ISNULL(LAG(price) OVER (ORDER BY ID),0) AS new_value
FROM #tmp
)
UPDATE t
SET diff_with_prev_prive = t.new_value
FROM cte t
SELECT * FROM #tmp

Return all records if more than 2/3 satisfy a value

I have a table representing multiple transactions by customers in any given day. I need to return all transactions per customer if two thirds or more of the transactions per customer were cash instead of credit card.
In the example below I want to return all of customers' 1, 4 transactions as they were the only customers to have 2 thirds or more of their transactions as cash:
+----------------+-------------+-----------------+------------------+
| Transaction ID | CustomerNum | TransactionType | TransactionValue |
+----------------+-------------+-----------------+------------------+
| 1 | 1 | Cash | 11 |
| 2 | 1 | Card | 12 |
| 3 | 1 | Cash | 13 |
| 4 | 2 | Cash | 14 |
| 5 | 2 | Card | 15 |
| 6 | 3 | Cash | 15 |
| 7 | 3 | Card | 11 |
| 8 | 3 | Cash | 12 |
| 9 | 3 | Card | 13 |
| 10 | 4 | Cash | 14 |
| 11 | 4 | Cash | 15 |
| 12 | 4 | Cash | 15 |
+----------------+-------------+-----------------+------------------+
This seems to work with the sample data:
declare #t table (TranID int not null,CustomerNum int not null,
TranType varchar(17) not null,TranValue decimal(18,0) not null)
insert into #t(TranID,CustomerNum,TranType,TranValue) values
( 1,1,'Cash',11), ( 2,1,'Card',12), ( 3,1,'Cash',13),
( 4,2,'Cash',14), ( 5,2,'Card',15),
( 6,3,'Cash',15), ( 7,3,'Card',11), ( 8,3,'Cash',12), ( 9,3,'Card',13),
(10,4,'Cash',14), (11,4,'Cash',15), (12,4,'Cash',15)
;With Counted as (
select *,
COUNT(*) OVER (PARTITION BY CustomerNum) as cnt,
SUM(CASE WHEN TranType='Cash' THEN 1 ELSE 0 END)
OVER (PARTITION BY CustomerNum) as cashcnt
from #t
)
select * from Counted
where cashcnt * 3 >= cnt * 2
I've gone with simple multiplication at the end to keep all of the maths as integers and avoid having to think about float/decimal and the representation of 2/3.
Result:
TranID CustomerNum TranType TranValue cnt cashcnt
----------- ----------- ----------------- ----------- ----------- -----------
1 1 Cash 11 3 2
2 1 Card 12 3 2
3 1 Cash 13 3 2
10 4 Cash 14 3 3
11 4 Cash 15 3 3
12 4 Cash 15 3 3
Try this:
select t.*
from (select customernum
from transactions
group by customernum
having sum(case when TransactionType = 'Cash' then 1.0 else 0.0 end) / sum(1.0) > 0.6666) c
join transactions t on t.customernum = c.customernum

SQL: sorting child rows under parent row

I have this table in my SQL Server database.
MenuID MenuText ParentMenu MenuOrder MenuLevel
-------------------------------------------------------------
1 Home 0 1 0
2 Administrator 0 2 0
3 Groups 2 1 1
4 Users 2 2 1
5 Permissions 2 3 1
6 Test Level2 3 1 2
7 Test Level3 6 1 3
I want to sort this table rows like this:
Home, Administrator are MenuLevel 0 items.
MenuLevel 1 items will be under its top level menus according to their ParentMenu column which points to the parent MenuID. The child items will be sorted according to their MenuOrder column.
I tried so many things, but couldn't get the idea how it will be done using query.
Try this one:
SQL Fiddle
;WITH Cte AS(
SELECT *,
DisplayOrder = CAST(ROW_NUMBER() OVER(PARTITION BY ParentMenu ORDER BY MenuOrder) AS VARCHAR(MAX))
FROM Test
WHERE
ParentMenu = 0
UNION ALL
SELECT
t.MenuID,
t.MenuText,
t.ParentMenu,
t.MenuOrder,
t.MenuLevel,
DisplayOrder = c.DisplayOrder + CAST(ROW_NUMBER() OVER(PARTITION BY t.ParentMenu ORDER BY t.MenuOrder) AS VARCHAR(MAX))
FROM Cte c
INNER JOIN Test t
ON c.MenuID = t.ParentMenu
)
SELECT
MenuID,
MenuText,
ParentMenu,
MenuOrder,
MenuLevel
FROM cte
ORDER BY DisplayOrder
Result:
| MenuID | MenuText | ParentMenu | MenuOrder | MenuLevel |
|--------|---------------|------------|-----------|-----------|
| 1 | Home | 0 | 1 | 0 |
| 2 | Administrator | 0 | 2 | 0 |
| 3 | Groups | 2 | 1 | 1 |
| 6 | Test Level2 | 3 | 1 | 2 |
| 7 | Test Level3 | 6 | 1 | 3 |
| 4 | Users | 2 | 2 | 1 |
| 5 | Permissions | 2 | 3 | 1 |