Using sql to recursively generate values depending on keys and column values - sql

I have a table like this:
store
item
value
store1
item1
2
store1
item2
3
I want to use it to create the following table:
store
item
value
store1
item1
0
store1
item1
1
store1
item1
2
store1
item2
0
store1
item2
1
store1
item2
2
store1
item2
3
I'm aware that I can do with recursive n(n) as (select 0 n union all select n + 1 from n limit 40) and then later a cross join if the upper limit was constant (40) but in my case, it changes by (store, item). I was wondering if this can be done in SQL or if it is better to figure it out in python. Thanks.

You can use a recursive CTE. The syntax is something like this:
with recursive cte(store, item, value, n) as (
select store, item, value, 0
from t
union all
select store, item, value, n + 1
from cte
where n < value
)
select store, item, n as value
from cte;
The exact syntax for recursive CTEs varies, depending on the database. This appears to be similar to the code you have in your question.
Here is a db<>fiddle.

Related

Create multiple rows based on 1 column

I currently have a table with a quantity in it.
ID Code Quantity
1 A 1
2 B 3
3 C 2
4 D 1
Is there anyway to write a sql statement that would get me
ID Code Quantity
1 A 1
2 B 1
2 B 1
2 B 1
3 C 1
3 C 1
4 D 1
I need to break out the quantity and have that many number of rows
Thanks
Here's one option using a numbers table to join to:
with numberstable as (
select 1 AS Number
union all
select Number + 1 from numberstable where Number<100
)
select t.id, t.code, 1
from yourtable t
join numberstable n on t.quantity >= n.number
order by t.id
Online Demo
Please note, depending on which database you are using, this may not be the correct approach to creating the numbers table. This works in most databases supporting common table expressions. But the key to the answer is the join and the on criteria.
One way would be to generate an array with X elements (where X is the quantity). So for rows
ID Code Quantity
1 A 1
2 B 3
3 C 2
you would get
ID Code Quantity ArrayVar
1 A 1 [1]
2 B 3 [1,2,3]
3 C 2 [2]
using a sequence function (e.g, in PrestoDB, sequence(start, stop) -> array(bigint))
Then, unnest the array, so for each ID, you get a X rows, and set the quantity to 1. Not sure what SQL distribution you're using, but this should work!
You can use connect by statement to cross join tables in order to get your desired output.
check my solution it works pretty robust.
select
"ID",
"Code",
1 QUANTITY
from Table1, table(cast(multiset
(select level from dual
connect by level <= Table1."Quantity") as sys.OdciNumberList));

Querying columnar data in SQL Server

I have a table structured as shown below
CustimerId Name Store Bill Item1 Item2 Item3
1 A StoreA 100 Y
2 A StoreB 200 N Y
3 B StoreA 300 N
4 C StoreC 400 Y N Y
Now I want to make one column based on the values in columns Item1, Item2, Item3 which is as below.
CustimerId Name Store Bill BoughtAnySpecialItem
1 A StoreA 100 Y
2 A StoreB 200 Y
3 B StoreA 300 N
4 C StoreC 400 Y
This means if any of the value in columns through Item1 to Item3 is 'Y' I want to make it 'Y' otherwise 'N'. The Item columns are more and or of different name(not like Item-X).
I need the query to do this. Can anyone suggest the best way to do this in SQL server? or at least point me to the right resource. Thanks in advance.
You can use case statement to do that, something like this:
case when Item1 = 'Y' or Item2 = 'Y' or Item3 = 'Y' then 'Y' else 'N' end
Since you have more column's to check you can reverse the IN operator
SELECT *,
CASE WHEN 'Y' in (Item1 , Item2 , Item3) THEN 'Y' ELSE 'N' END as BoughtAnySpecialItem
FROM Yourtable
Now you can easily list the column's in IN opertaor

One column condition in sql

I have a table:
[letter] [Name] [status] [price]
A row1 1 11
A row1 1 15
B row2 2 9
B row2 3 23
B row2 3 30
And want to select data something like this:
SELECT letter, Name,
COUNT(*),
CASE WHEN price>10 THEN COUNT(*) ELSE NULL END
GROUP BY letter, Name
the result is:
A row1 2 2
B row2 1 null
B row2 2 2
But I want this format:
A row1 2 2
B row2 3 2
Please, help me to modify my query
Close. Probably want this instead:
SELECT letter, Name,
COUNT(*),
SUM(CASE WHEN price>10 THEN 1 ELSE 0 END)
FROM TableThatShouldHaveAppearedInTheQuestionInTheFromClause
GROUP BY letter, Name
should work. Assuming that the intention of the fourth column is to return the count of the number of rows, within each group, with a price greater than 10. It's also possible to do this as a COUNT() over a CASE then returns non-NULL and NULL results for the rows that should and should not be counted, but I find the above form easier to quickly reason about.
Since nulls are not used in aggregate functions:
SELECT letter
, name
, count(*)
, count(
case when price > 10 then 1
end
)
FROM t
GROUP BY letter, name
You were very close.
Looking to the other answers, probably this is not the best way, but it will work.
The count of the prices over 10 is made with a subquery which has a condition on price > 10 and which is joined to the current TAB record with the alias A for the same letter and name.
SELECT letter,
Name,
COUNT(*),
(SELECT COUNT(*) FROM TAB WHERE letter = A.letter and Name = A.Name WHERE price>10)
FROM TAB A
GROUP BY letter, Name

How to express this query in SQL Server 2008

I have table called Reporting with following columns
OutletId CampaignId ItemId Qty
10 1 Item1 12
10 1 Item2 13
10 1 Item3 14
20 2 Item4 10
20 2 Item5 11
20 2 Item6 12
20 2 Item7 8
Now I want to retrieve the data in this format
when user select campaignId =1
OutletId CampaignId Item1 Item2 Item3
10 1 12 13 14
when user select CampaignId=2
OutletId CampaignId Item4 Item5 Item6 Item7
20 2 10 11 12 8
Here Items for campaign are not fixed
I think it is efficient in this way:
SELECT *
FROM
(
SELECT OutletId, CampaignId, ItemId, Qty
FROM Reporting) AS p
PIVOT
(
SUM(Qty)
FOR ItemId IN (SELECT ItemId FROM Reporting WHERE campaignId =1)
) as pvt
Comment: Here campaignId =1 or campaignId =2 or campaignId =... whatever u want
A possible solution would be:
SELECT *
FROM
(
SELECT OutletId, CampaignId,ItemId, Qty
FROM test) AS p
PIVOT
(
SUM(Qty)
FOR ItemId IN (Item1,Item2,Item3,Item4)
) as pvt
But obviously, as commented before is not very efficient because you don't always know the Items... you either redesign your table or if using PIVOT you can build a dynamic sql building pivot items previously.

ACCESS/SQL: Calculating the difference between rows (not dates)

I looked through the questions here but didn't find one that suited my case.
I'm trying to write a query that will output the difference between rows
Here is a table:
ITEM CYCLES
--------------------
itemA 5
itemA 17
itemA 20
itemA 22
itemB 26
itemB 30
itemB 37
it is actually obtained by a query, and with an order by (item, cycles)
here is what I'd like the query to give me:
itemA 12
itemA 3
itemA 2
itemB 4
itemB 7
I have absolutely no idea how to proceed in SQL. Is it even possible ?
or do I have to write a function ?
*****************************EDIT*********************************
I appologize for the lack of precision, and even some absurd mistakes. I was rushing out and wrote hastily :/
I'm analyzing item failures, and need to output cycles between failures.
ITEM column is just the item ID, and
CYCLES is the number of cycles the item had when the failure occurred.
And actually looking at it today I don't understand why I put that middle column (A,B,C...) which I don't have in my table.
And indeed, I don't need to output zero values, but there shouldn't be any anyway.
I'll try the provided solutions and will get back; thanks for the answers !
Here's an updated solution, based on the changes to your question. Note that you'll need to change Qry to the name of your query:
SELECT Qry.Item, Qry.Cycles - (SELECT TOP 1 Cycles FROM Qry AS Q
WHERE Qry.Item=Q.Item
AND Q.Cycles < Qry.Cycles
ORDER BY Q.Cycles DESC) AS Diff
FROM Qry
WHERE (SELECT TOP 1 Cycles FROM Qry AS Q
WHERE Qry.Item=Q.Item AND Q.Cycles < Qry.Cycles) Is Not Null
ORDER BY Qry.Item, Qry.Cycles;
This produces the following output:
Item Diff
itemA 12
itemA 3
itemA 2
itemB 4
itemB 7
I assume that the 6 in your sample output was a typo, since 30 - 26 = 4.
Assuming our columns are named ItemName, Letter, Num, something like the following might do it:
SELECT T1.ItemName, T1.Letter
, T1.Num, [T2].[Num]-Nz([T1].[Num],[T2].[Num]) AS Expr1
FROM Table1 AS T1
LEFT JOIN Table1 AS T2
ON (T1.ItemName = T2.ItemName
And Asc([T1].[Letter]) = Asc([T2].[Letter]) - 1 )
Where [T2].[Num] <> Nz([T1].[Num],[T2].[Num])
Note that you cannot create this using the QBE grid. You would need to create in code or in the SQL View.
How about:
SELECT b.Item, b.[No], (
SELECT Top 1 a.No
FROM items a
WHERE a.No > b.No
ORDER BY a.Item,a.No) AS NextNo,
[NextNo]-[No] AS Result
FROM items AS b;
This is much simpler if you have a fourth column with an ID:
Col0 Col1 Col2 Col3
1 itemA A 5
2 itemA B 17
3 itemA C 20
4 itemA D 22
5 itemB A 26
6 itemB B 30
7 itemB C 37
Then you can use the following query:
SELECT Tbl.Col1, Tbl.Col3 - Prev.Col3 AS Diff
FROM Tbl INNER JOIN Tbl AS Prev
ON Tbl.Col0 - 1 = Prev.Col0
AND Tbl.Col1 = Prev.Col1
To produce exactly what you requested:
Col1 Diff
itemA 12
itemA 3
itemA 2
itemB 4
itemB 7
You would need to manage the Col0 data to make sure you don't end up with gaps (ie, Autonumber field would be a bad idea).
All of the above having been said, if you let us know what you are really trying to accomplish I'm thinking there is probably a better way to do what you want.