Insert into temp table from SELECT giving strange error - sql-server

EDIT: I have since been corrected that I need to be doing an update to achieve my intended results. At this point, I'm not able to get the UPDATE statement to do what I want it to, despite my select statement giving me exactly what I want.
begin tran
update #cal1
set calendar_key =
(
SELECT 16801 - 1 + ROW_NUMBER() OVER (
PARTITION BY 1 ORDER BY CalendarDate
) )
FROM #cal1
The results from this give me a repeating 16801 in this column as opposed to going down through the numbers as in my select statement. What am I doing wrong here?

Well, the error seems rather clear and obvious: since you're not specifying the Holiday column in your INSERT statement (in the list of columns you're inserting into), no value is being inserted into Holiday - this is stays NULL.
And it appears from the error message that the Holiday does not allow NULL - you need to explicitly provide a value!
INSERT INTO #cal1 (calendar_key, Holiday)
SELECT
16801 - 1 + ROW_NUMBER() OVER (PARTITION BY 1 ORDER BY CalendarDate) AS count,
-some-value-for-holiday-here-
FROM #cal1
Other than explicitly providing a value, you could also:
Make the Holiday column nullable, so it would be fine with a NULL
Provide a default constraint for Holiday, so that if you don't provide a value, the defined default will be used instead
UPDATE:
Seeing that you say
The problem here is that the other values are as they should be. Is there not a way I can add data to just this one column?
Do you really want to INSERT a new row?? Or would you much rather want to UPDATE an existing row and set just that calendar_key column's value?? In that case, you need an UPDATE statement - not an INSERT ......

Related

Is it possible to start over auto-increment each day, with composite keys: date + int? (MSSQL)

Lets say i have a small db table with only two fields. (MSSQL) Like this:
date (Date) daily_counter (Int)
-------------------------
2021-07-18 0
2021-07-18 1
2021-07-18 2
2021-07-19 0
I want to insert a new fifth row and insert value "2021-07-19" to the date field. And i want to know what the daily_counter is for my new row.
As you perhaps understand by the example, the daily_counter is supposed to auto increase, starting over each day.
So, since there is already a row with that date with the value 0 on the field daily_counter. I want to add 1 to daily_counter for my new row without sending the value 1 to the query.
How can i think when designing this type of table and data. Any hints appreciated. Thanks!
Kr
Gustav
UPDATE:
Ok, i think i got something that could work. The only downside would be when deleting and adding new rows, as the new id could be previosly used and deleted and added again.
A side from that i think i got something that i can use.
It might not be pretty now, but it looks like this.
It seems to work also when there is no row for the current day.
DECLARE #date DATE
SET #date = '2021-07-22'
DECLARE #daily_counter INT
SET #daily_counter = (SELECT MAX(daily_counter) from mytable where date = #date);
SET #daily_counter = #daily_counter + 1;
IF #daily_counter IS NULL
BEGIN
SET #daily_counter = 1;
END
INSERT INTO
mytable
(date, daily_counter)
OUTPUT #daily_counter
VALUES (#date, #daily_counter)
Thanks again for the help!
It's not possible to make the database do this automatically in the row itself. You must have a single counter across all dates (a SEQUENCE would be good for this).
What you can do is use the row_number() function to simulate this at the point where you query the data:
SELECT *, row_number() over (partition by [date] order by [date])
FROM ...
Unfortunately, this will still fail if you need to preserve the original position following deletes, but there's not a good way to do this right now in a database without triggers or application code.

Using Set and Between in the same query to populate a table

What I am trying to do is populate a row using a counter or some function of sql I am unaware of.
I can use
INSERT INTO EmpSkillsBridge
(EmpIdFK , EmpSkillFK)
Values
(1,1);
This updates one entire record I can even do it in batches of parentheses.
However since I am setting up a the table for the first time and since the data is for test (cough homework cough) reasons. I am trying make it so that the I can create a whole batch of data I have 200 EmpID and 6 different skills. all I want right now is to make the first 25 EmpIdFK and the EmpSkillFK be (1,1)
If i use a where empIdFk < 26 I get an error.
I tried using a loop but being new I got a little lost on how to implement
Then I read I could use the between statement. so my question is can I use a set statement in conjuction with between and make the code work that way?
Set into EmpSkillsBridge
(EmpIdFK , EmpSkillFK)
WHERE (EMPID BETWEEN 1 AND 26)
Values
(1,1);
would this be the best way to go around that?
You can do this:
Insert into EmpSkillsBridge
Select empid, 1
from employees
where empid between 1 and 25;
Something like this should work for you:-
insert into EmpSkillsBridge
(EmpID, EmpSkillsFK)
select empid, 1 from
(
select ROW_NUMBER() over(order by number) empid
from master..spt_values
) v
where empid between 1 and 25

How can this expression reach the NULL expression?

I'm trying to randomly populate a column with values from another table using this statement:
UPDATE dbo.SERVICE_TICKET
SET Vehicle_Type = (SELECT TOP 1 [text]
FROM dbo.vehicle_typ
WHERE id = abs(checksum(NewID()))%21)
It seems to work fine, however the value NULL is inserted into the column. How can I get rid of the NULL and only insert the values from the table?
This can happen when you don't have an appropriate index on the ID column of your vehicle_typ table. Here's a smaller query that exhibits the same problem:
create table T (ID int null)
insert into T(ID) values (0),(1),(2),(3)
select top 1 * from T where ID = abs(checksum(NewID()))%3
Because there's no index on T, what happens is that SQL Server performs a table scan and then, for each row, attempts to satisfy the where clause. Which means that, for each row it evaluates abs(checksum(NewID()))%3 anew. You'll only get a result if, by chance, that expression produces, say, 1 when it's evaluated for the row with ID 1.
If possible (I don't know your table structure) I would first populate a column in SERVICE_TICKET with a random number between 0 and 20 and then perform this update using the already generated number. Otherwise, with the current query structure, you're always relying on SQL Server being clever enough to only evaluate abs(checksum(NewID()))%21once for each outer row, which it may not always do (as you've already found out).
#Damien_The_Unbeliever explained why your query fails.
My first variant was not correct, because I didn't understand the problem in full.
You want to set each row in SERVICE_TICKET to a different random value from vehicle_typ.
To fix it simply order by random number, rather than comparing a random number with ID. Like this (and you don't care how many rows are in vehicle_typ as long as there is at least one row there).
WITH
CTE
AS
(
SELECT
dbo.SERVICE_TICKET.Vehicle_Type
CA.[text]
FROM
dbo.SERVICE_TICKET
CROSS APPLY
(
SELECT TOP 1 [text]
FROM dbo.vehicle_typ
ORDER BY NewID()
) AS CA
)
UPDATE CTE
SET Vehicle_Type = [text];
At first we make a Common Table Expression, you can think of it as a temporary table. For each row in SERVICE_TICKET we pick one random row from vehicle_typ using CROSS APPLY. Then we UPDATE the original table with chosen rows.

SQL Server 2005 SELECT TOP 1 from VIEW returns LAST row

I have a view that may contain more than one row, looking like this:
[rate] | [vendorID]
8374 1234
6523 4321
5234 9374
In a SPROC, I need to set a param equal to the value of the first column from the first row of the view. something like this:
DECLARE #rate int;
SET #rate = (select top 1 rate from vendor_view where vendorID = 123)
SELECT #rate
But this ALWAYS returns the LAST row of the view.
In fact, if I simply run the subselect by itself, I only get the last row.
With 3 rows in the view, TOP 2 returns the FIRST and THIRD rows in order. With 4 rows, it's returning the top 3 in order. Yet still top 1 is returning the last.
DERP?!?
This works..
DECLARE #rate int;
CREATE TABLE #temp (vRate int)
INSERT INTO #temp (vRate) (select rate from vendor_view where vendorID = 123)
SET #rate = (select top 1 vRate from #temp)
SELECT #rate
DROP TABLE #temp
.. but can someone tell me why the first behaves so fudgely and how to do what I want? As explained in the comments, there is no meaningful column by which I can do an order by. Can I force the order in which rows are inserted to be the order in which they are returned?
[EDIT] I've also noticed that: select top 1 rate from ([view definition select]) also returns the correct values time and again.[/EDIT]
That is by design.
If you don't specify how the query should be sorted, the database is free to return the records in any order that is convenient. There is no natural order for a table that is used as default sort order.
What the order will actually be depends on how the query is planned, so you can't even rely on the same query giving a consistent result over time, as the database will gather statistics about the data and may change how the query is planned based on that.
To get the record that you expect, you simply have to specify how you want them sorted, for example:
select top 1 rate
from vendor_view
where vendorID = 123
order by rate
I ran into this problem on a query that had worked for years. We upgraded SQL Server and all of a sudden, an unordered select top 1 was not returning the final record in a table. We simply added an order by to the select.
My understanding is that SQL Server normally will generally provide you the results based on the clustered index if no order by is provided OR off of whatever index is picked by the engine. But, this is not a guarantee of a certain order.
If you don't have something to order off of, you need to add it. Either add a date inserted column and default it to GETDATE() or add an identity column. It won't help you historically, but it addresses the issue going forward.
While it doesn't necessarily make sense that the results of the query should be consistent, in this particular instance they are so we decided to leave it 'as is'. Ultimately it would be best to add a column, but this was not an option. The application this belongs to is slated to be discontinued sometime soon and the database server will not be upgraded from SQL 2005. I don't necessarily like this outcome, but it is what it is: until it breaks it shall not be fixed. :-x

Inserting the values with condition

Using SQL Server 2005
When i insert the date it should compare the date in the table.
If it is equal with other date, it should display a error message and also it should allow only to insert the next date.
For Example
Table1
Date
20091201
20091202
Insert into table1 values('20091202')
The above query should not allow to insert the same value
Insert into table1 values('20091204')
The above query also should not allow to insert the long gap date.
The query should allow only the next date.
It should not allow same date and long gap date.
How to insert a query with this condition.
Is Possible in SQL or VB.Net
Need SQL Query or VB.Net code Help
You could use a where clause to ensure that the previous day is present in the table, and the current day is not:
insert into table1 ([dateColumn])
select '20091204'
where exists (
select * from table1 where [dateColumn] = dateadd(d,-1,'20091204')
)
and not exists (
select * from table1 where [dateColumn] = '20091204'
)
if ##rowcount <> 1
raiserror ('Oops', 16, 1)
If the insert succeeds, ##rowcount will be set to 1. Otherwise, an error is returned to VB using raiserror.
Why not just have a table of dates set up in advance, and update a row once you want to "insert" that date?
I'm not sure I understand the point of inserting a new date only once, and never allowing a gap. Could you describe your business problem in a little more detail?
Of course you could use an IDENTITY column, and then have a computed column or a view that calculates the date from the number of days since (some date). But IDENTITY columns do not guarantee contiguity, nor do they even guarantee uniqueness on their own (unless you set up suc a constraint separately).
Preventing duplicates should be done at the table level with a unique constraint, not with a query. You can check for duplicates first so that you can handle errors in your own way (rather than let the engine raise an exception for you), but that shouldn't be your only check.
Sounds like your date field should just be unique with auto-increment.

Resources