Inserting values into new column based on a LEAD () function - sql

I have a column called Sales and I created a column sales_next. I want to base each row's value in sales_next using a LEAD function. I got this so far, but I for some reason can't figure out how to update every row.
INSERT INTO superstore_sales$ (sales_next)
VALUES
(
(SELECT TOP 1
LEAD(sales, 1) OVER (
ORDER BY sales
) AS sales_next
FROM superstore_sales$
WHERE sales_next IS NULL
))
I have tried removing the TOP 1 and I get the multiple values in subquery error, of course because I am unsure how to tell SQL to grab one value for each row using the LEAD function.

A derived table (or a CTE) is updatable, if I understand what you're trying to do this should work:
update t set sales_next = sn
from (
select sales_next, Lead(sales, 1) OVER (ORDER BY sales) AS sn
from superstore_sales$
where sales_next IS null
)t;

I figured it out.Aaron was right, no need to create the column physically. I only need to do it query time. I figured out how both LEAD and LAG operate and the final code was simple:
SELECT "Order ID", "Customer Name", Sales,
LEAD(Sales) OVER(ORDER BY Sales) as next_sales
FROM superstore_sales$;
Thank you for your help:)

Related

SQL Query for multiple columns with one column distinct

I've spent an inordinate amount of time this morning trying to Google what I thought would be a simple thing. I need to set up an SQL query that selects multiple columns, but only returns one instance if one of the columns (let's call it case_number) returns duplicate rows.
select case_number, name, date_entered from ticket order by date_entered
There are rows in the ticket table that have duplicate case_number, so I want to eliminate those duplicate rows from the results and only show one instance of them. If I use "select distinct case_number, name, date_entered" it applies the distinct operator to all three fields, instead of just the case_number field. I need that logic to apply to only the case_number field and not all three. If I use "group by case_number having count (*)>1" then it returns only the duplicates, which I don't want.
Any ideas on what to do here are appreciated, thank you so much!
You can use ROW_NUMBER(). For example
select *
from (
select *,
row_number() over(partition by case_number) as rn
) x
where rn = 1
The query above will pseudo-randomly pick one row for each case_number. If you want a better selection criteria you can add ORDER BY or window frames to the OVER clause.

SQL Equivalent to LAG() to create a computed table

I'm fairly new to SQL and I'm trying to create a computed column in a table that calculates the DateDiff on a column between the current row and the previous row.
Now all is fine and dandy doing a query with Select to display this value:
SELECT *,
Case When INCM<> lag(INCM) over(ORDER BY INCM ASC, Submit_Date ASC) Then 0 else DateDiff(mi,Submit_Date, lag(Submit_Date) over (ORDER BY INCM ASC, Submit_Date ASC)) End As Diff
FROM [OP].[Ticket_Work_Info]
But as I recently found out, when adding a computed column with this logic to the existing table I get an error saying Windowed Functions can only be used with Select or Order By.
I've been searching everywhere for an equivalent to LAG to use to create a computed column on a table.
In the meantime I ended up creating a view with this piece of code, but that's not really what I want to do going forward.
Can someone give me a hand?
Regards,
Here is some reasoning on why computed columns can only refer to values in the current row (and deterministic functions and constants). Consider a definition such as:
create t (
t_id int,
a varchar(255),
x int,
prev_x as (lag(x) over (order by t_id)
);
And some sample data:
id y x
1 z 6
2 abc 28
3 z 496
This looks fine. But, consider this query:
select t.*
from t
where a <> 'abc';
Should the value of x_prev for the third row be 28 or 6?
I guess no one wanted to make a decision on this. Instead, the idea is that a row is well-defined, so the filtering conditions do not affect the values within a row.

SQL Query - Rank showing only 1 rank for all records

I am trying to perform ranking based on some calculation of already existing columns. I tried using the SQL RANK() function however it is showing the result as 1 for all entries even if the value of the order by (score column) is different. Please see the details below:
qu_point and ti_points are calculated columns
score column is again a derived column, however, simply sum of two columns mentioned in point 1.
I have used the SQL query as follow:
use EFR_DB
GO
select d.serial, d.question_set_id, d.correct_answers, d.total_questions, d.time_taken_seconds, q.total_time_in_secs,
(cast(d.correct_answers as float)/d.total_questions) as qu_point, ((q.total_time_in_secs-d.time_taken_seconds)/q.total_time_in_secs) as ti_point,
(((cast(d.correct_answers as float)/d.total_questions)*2) + ((q.total_time_in_secs-d.time_taken_seconds)/q.total_time_in_secs)) as score,
rank() over (partition by d.question_set_id order by score)
from daily_quiz_record d join Question_set q
on q.question_set_id=d.question_set_id
Please help me how can I do the raking which is partitioned by question_set_id and ranked on the basis of the score.
Screenshot attached for your reference.
enter image description here
You can’t use an alias defined in the select clause in the same clause. I suppose that one of your table has a column called score, otherwise your query would error - so this existing column is being used for ordering instead of the computed value.
Since your expression is lengthy, it is simpler to turn the query to a subquery, and rank in the outer query:
select
t.*,
rank() over(partition by question_set_id order by score) rn
from (
-- your existing query (without rank)
) t

Incorporate a concatenation and count in a SQL update command?

I am looking for a way to update records so each entry adds 1 to the end of the string. In my case, I'm trying to update a field named FiberID. Each Record should have JCK0.R000.Ax, where x is equal to 1,2,3...,24.
Ideal result:
FiberID
JCK0.R000.A1
JCK0.R000.A2
JCK0.R000.A3
... and so on until it reaches A24.
Here is an example of the data.
This seems so useful that I'm sure it has been discussed here before, but for what ever reason I'm not seeing anything.
You could use an row_number and an updatable CTE:
with cte as (
select
fiber_id,
concat(
fiber_id,
'.A',
cast(row_number() over (partition by fiber_id order by id) as varchar(2))
) new_fiber_id
from mytable
)
update cte set fiber_id = new_fiber_id
This assumes that you have a column called id that can be used to order records having the same fiber_id.
Side note: it is unclear why you should have exactly 24 numbers per fiber_id, and you sample data does not describes that. This will assign increasing numbers to duplicate fiber_ids, regardless of how many there are.

Simple select all columns of min value -> error

I'd want a query to fetch all columns of a tuple according to a column's lowest value in the table:
SELECT *, MIN(Opened) FROM Account
And for some reason this throws an error:
ORA-00923: FROM keyword not found where expected
This is basic stuff in my opinion, coming from SQLite, but Oracle is new to me.
I created a table like:
CREATE TABLE Account (
Act_id INTEGER,
Opened DATE,
Balance NUMBER(10,2),
CONSTRAINT act_pk PRIMARY KEY(act_id)
)
If I enter any other column name (e.g. SELECT balance, MIN(opened)), it gives another error:
ORA-00937: not a single-group group function
According to this tutorial at least the * notation is in Oracle. And the MIN works by itself. Am I missing something?
SQLite is "broken" with respect to the syntax that you learned. It is the only database that returns the entire row of the minimum or maximum in such a case. There are simple ways to do this, so I'm not sure why the designers felt the need to include that particular (mis)feature.
Just use order by and rownum if you want one row:
select a.*
from (select a.*
from account a
order by opened asc
) a
where rownum = 1;
If you want all rows with the minimum value, use a subquery:
select a.*
from account a
where a.opened = (select min(a2.opened) from account a2);
When you're asking for BALANCE and MIN(OPENED) you need to specify a GROUP BY, as in
SELECT BALANCE, MIN(OPENED)
FROM ACCOUNT
GROUP BY BALANCE
Earlier you said "I'd want a query to fetch all columns of a tuple according to a column's lowest value in the table". My interpretation of this is that you need a WHERE clause:
SELECT *
FROM ACCOUNT
WHERE OPENED = (SELECT MIN(OPENED) FROM BALANCE)
Or perhaps you meant you wanted all the columns from ACCOUNT, along with the min value of OPENED for the entire table. In that case I'd suggest a CROSS JOIN
SELECT *
FROM ACCOUNT
CROSS JOIN (SELECT MIN(OPENED) AS MIN_OPENED FROM ACCOUNT)
Best of luck.