SQL: Auto Increment value during insert - sql-server-2008

I have the an existing table that for some reason the designer decided to manually control the Primary Key value by storing the last used value in a seperate table (changing the table to use Identity is not an option right now).
I now need to do a mass update to this table as follows:
DECLARE #NeedFieldID int
SET #NeedFieldID = 62034
INSERT INTO T_L_NeedField (NeedID, NeedFieldID, FieldName, Sequence, DisplayAs, FieldPrompt, DataType, ValidOptions, IsRequiredForSale)
(
SELECT
DISTINCT n.NeedID,
#NeedFieldID + 1,
'DetailedOutcome',
999,
'Detailed Outcome',
'Select appropriate reason for a No Sale outcome',
'T',
'Pricing, Appointment Date / Time Not Available, Will Call Back, Declined',
0
FROM T_L_Need n
INNER JOIN T_L_NeedField nf
ON n.NeedID = nf.NeedID
WHERE (n.Need LIKE 'Schedule%' AND n.Disabled = 0)
)
Obviously '#NeedFieldID + 1' doesn't work (just using it to show what I want to do). How can I increment #NeedFieldID as SQL inserts the values for each of the distinct NeedId's? I am using SQL Server 2008.

You want row_number():
DECLARE #NeedFieldID int
SET #NeedFieldID = 62034
INSERT INTO T_L_NeedField (NeedID, NeedFieldID, FieldName, Sequence, DisplayAs, FieldPrompt, DataType, ValidOptions, IsRequiredForSale)
(
SELECT
DISTINCT n.NeedID,
#NeedFieldID + row_number() over (order by n.NeedID),
'DetailedOutcome',
999,
'Detailed Outcome',
'Select appropriate reason for a No Sale outcome',
'T',
'Pricing, Appointment Date / Time Not Available, Will Call Back, Declined',
0
FROM T_L_Need n
INNER JOIN T_L_NeedField nf
ON n.NeedID = nf.NeedID
WHERE (n.Need LIKE 'Schedule%' AND n.Disabled = 0)
)
However, your best bet is to make NeedFieldID an identity column and just let SQL Server do the work for you.

Related

Generate random number that not exists on a table, then use it on a concat function on MySQL

The problem is that when I'm inserting data on tbl_tramite, the query doesn't recognize random_number value. It says unknown column 'random_number' in "IN/ALL/ANY" subquery. All of this using MySQL.
This is for a C# Winforms application with MariaDB 10.3, this query inserts on 2 tables but random_number is inserted on tbl_tramite.
START TRANSACTION;
INSERT IGNORE tbl_solicitante (cedula, solicitante, telefono, distrito, comunidad, estado)
VALUES(ced, sol, tel, dist, com, 'A');
SELECT LPAD(FLOOR(RAND() * 999999.99), 4) AS random_number
FROM tbl_tramite
WHERE random_number NOT IN (SELECT id_caso FROM tbl_tramite) LIMIT 1;
SET #caso := CONCAT(ced, random_number);
INSERT INTO tbl_tramite (id_caso, solicitante_cedula, usuario_id_usuario, operacion_id_operacion, inspector, detalle, estado,
fecha_ingreso, fecha_regreso_inspeccion, fecha_aprobacion, fecha_entrega)
VALUES(#caso , ced, func, tra, insp, det, est, fech1, fech2, fech3, fech4);
COMMIT;
Tested this fragment only:
SELECT LPAD(FLOOR(RAND() * 999999.99), 4) AS id_tramite
FROM tbl_tramite
WHERE id_tramite NOT IN (SELECT id_tramite FROM tbl_tramite);
Doesn`t return any record.
I expect random_number to be recognized and then to use it to concatenate it with a unique id so the other insert statement uses it. I'm receiving a message from try-catch on c# that says that random_number column field list doesn't exist.
I solved this using this statement:
SET #valor = (SELECT NUM FROM(SELECT LPAD(FLOOR(RAND() * 999999.99), 7) AS NUM) AS SUBQ WHERE NUM NOT IN (SELECT id_caso FROM tbl_tramite) LIMIT 1);
I'm using HeidiSQL so I don't know if it was this or something else but in order to make it works I had to surround the query with parentheses.

SQL Server: How to query data all rows as Json object into next to other columns?

I have data like this:
I want to query result like this:
Here is my code
SELECT
PML_CODE
,PML_NAME_ENG
,(
SELECT
PML_ID
,PML_NO
,PML_CODE
,PML_NAME_ENG
,PML_FORMULA
FROM DSP.PARAMET_LIST AS A WITH(NOLOCK)
WHERE A.PML_ID = B.PML_ID
FOR JSON PATH, WITHOUT_ARRAY_WRAPPER
) AS BR_OBJECT
FROM DSP.PARAMET_LIST AS B WITH(NOLOCK)
My code works for what I want, but I want to know if there is a better, faster way to write this query?
Next time please do not post pictures, but rather try to create some DDL, fill it with sample data and state your own attempts and the expected output. This makes it easier for us to understand and to answer your issue.
You can try it like this:
DECLARE #tbl TABLE(PML_ID BIGINT, PML_NO INT, PML_CODE VARCHAR(10), PML_NAME_ENG VARCHAR(10), PML_FORMULA VARCHAR(10));
INSERT INTO #tbl VALUES
(2017102600050,1,'KHR','Riel','01')
,(2017102600051,2,'USD','Dollar','02')
,(2017102600052,3,'THB','Bath','05')
SELECT
PML_CODE
,PML_NAME_ENG
,BR_OBJECT
FROM #tbl
CROSS APPLY(
SELECT
(
SELECT
PML_ID
,PML_NO
,PML_CODE
,PML_NAME_ENG
,PML_FORMULA
FOR JSON PATH, WITHOUT_ARRAY_WRAPPER
)) AS A(BR_OBJECT);
The big difference to your own approach is that I use a CROSS APPLY using the columns we have already instead of calling a correlated sub-query.
You can just concatenate the values. Be sure to cast the integers and to handle the NULL values. For example, if there is NULL value for column, there can be two cases - ignore the property or add the property with null, right?
For SQL Server 2016 SP1+ and later you can use FOR JSON. Basically, you should end up with something like this:
DECLARE #DataSource TABLE
(
[PML_ID] VARCHAR(64)
,[PML_NO] INT
,[PML_CODE] VARCHAR(3)
,[PML_NAME_ENG] NVARCHAR(32)
,[PML_FORMULA] VARCHAR(2)
);
INSERT INTO #DataSource ([PML_ID], [PML_NO], [PML_CODE], [PML_NAME_ENG], [PML_FORMULA])
VALUES ('201710260000000050', 1, 'KHR', 'Riel', 01)
,('201710260000000051', 2, 'USD', 'Dollar', 02)
,('201710260000000052', 3, 'THB', 'Bath', 05);
SELECT [PML_CODE]
,[PML_NAME_ENG]
,'{"PML_ID":'+ [PML_ID] +',"PML_NO":'+ CAST([PML_NO] AS VARCHAR(12)) +',"PML_CODE":'+ [PML_CODE] +',"PML_NAME_ENG":'+ [PML_NAME_ENG] +',"PML_FORMULA":'+ [PML_FORMULA] +'}' AS [BR_OBJECT]
FROM #DataSource;
-- SQL Server 2016 SP1 and latter
SELECT DS1.[PML_CODE]
,DS1.[PML_NAME_ENG]
,DS.[BR_OBJECT]
FROM #DataSource DS1
CROSS APPLY
(
SELECT *
FROM #DataSource DS2
WHERE DS1.[PML_CODE] = DS2.[PML_CODE]
AND DS2.[PML_NAME_ENG] = DS2.[PML_NAME_ENG]
FOR JSON AUTO
) DS ([BR_OBJECT]);

MySql Insert Dynamic Query with Nested Select

I'm trying a MySQL Insert Query with mix of static & Dynamic Values. The INSERT command is.
INSERT INTO ebdb.requestaction(RequestID,
ActionID,
TransactionID,
IsActive,
IsComplete)
VALUES (
1,
**Dynamic Value from Below Query,
Dynamic Value from Below Query,**
1,
0);
The Query to fetch the field 2 & 3 come from the below Query.
SELECT transitionaction.TransitionID, transitionaction.ActionID
FROM transitionaction
INNER JOIN transition
ON transitionaction.TransitionID = transition.TransitionID
WHERE transition.TenantID = 1
AND transition.ProcessID = 1
AND transition.CurrentStateID = 1
ORDER BY transitionaction.TransitionID;
I'm doing something wrong in here.
Please guide me as to how this can be achieved in the most optimized way.
You can select static values as part of a query, e.g.:
INSERT INTO ebdb.requestaction(RequestID, ActionID, TransactionID, IsActive, IsComplete)
SELECT 1, transitionaction.ActionID, transitionaction.TransitionID, 1, 0
FROM transitionaction
INNER JOIN transition
ON transitionaction.TransitionID = transition.TransitionID
WHERE transition.TenantID = 1
AND transition.ProcessID = 1
AND transition.CurrentStateID = 1
ORDER BY transitionaction.TransitionID;
For more information, refer to MySQL's Insert...Select Syntax

Assigning a value from another database and function - MySQL variable scope

I am using MySQL to make a report showing the number of hours billed for a particular date range and project. The complexity is that the date range is variable for each project (different start month and start day). This information is coming from a value in another database/table.
I have the following UDF in MySQL:
DELIMITER //
CREATE FUNCTION TimeLeft(startday INT, today INT) RETURNS INT
DETERMINISTIC
BEGIN
DECLARE s INT;
IF startday < today THEN SET s = 0;
ELSE SET s = 1;
END IF;
RETURN s;
END //
DELIMITER;
I use that function in the following query, which is supposed to take the value returned in the TimeLeft function to determine the values for the start month (month(curdate())-#xx) and start day (#yy) for each project to calculate the hours:
AND time_records.record_date >= concat('2012/', month(curdate())-#xx , '/' , #yy)
Here's how I am setting the values for #xx and #yy:
SET #xx = 0; #this is the value that we will use to manipulate the month for the date range
SET #yy = 0;
#yy:= SELECT start_day_of_month FROM dashboard.client; #this doesn't seem to work
SELECT #xx:= TimeLeft(#yy,dayofmonth(curdate()));
I am getting some issues:
#yy is not getting the value - possibly my syntax is wrong?
The variables are set at the top of the code, so they are not getting changed for each project as they should be (there should be a different #xx and #yy for each project since each one has a different start and end date).
Here's the full query:
#below is where I assign the variables
SET #xx = 0; #this is the value that we will use to manipulate the month for the date range
SET #yy = 0;
#yy:= SELECT start_day_of_month FROM dashboard.client; #this doesn't seem to work
SELECT #xx:= TimeLeft(#yy,dayofmonth(curdate()));
# below is the MySQL query that is meant to use the variables assigned above
SELECT X.expr1 AS 'Project Name', #monthly_hours - SUM(X.expr2) AS 'Hours Billed
FROM
(SELECT
projects.name AS expr1
, sum(time_records.value) AS expr2
FROM project_objects
INNER JOIN projects
ON projects.id = project_objects.project_id
INNER JOIN time_records
ON time_records.parent_id = project_objects.id
WHERE time_records.parent_type = 'Task'
AND time_records.record_date >= concat('2012/', month(curdate())-#xx , '/' , #yy)
AND time_records.record_date <= curdate()
GROUP BY projects.name
UNION
SELECT
projects.name AS expr1
, sum(time_records.value) as expr2
FROM projects
INNER JOIN time_records
ON projects.id = time_records.parent_id
WHERE time_records.parent_type = 'Project'
AND time_records.record_date >= concat('2012/', month(curdate())-#xx , '/' , #yy)
AND time_records.record_date <= curdate()
GROUP BY projects.name) X
GROUP BY X.expr1
I think there is some issue of where I am assigning the variables #xx and #yy. These should be done for each individual Project, so putting them up on the top is probably not the best idea. I'm also not sure if I am assigning the #yy value correctly. It's supposed to query the value of the field of a table that is in another database but it keeps throwing a syntax error on the #yy assignment to that field.
Assign value to #yy inside select:
SELECT #yy:= start_day_of_month FROM dashboard.client;

Need to use a nested cursor and getting "invalid object name"

First, let me state right off that I'm well aware that cursors are generally evil and shouldn't be used - I'm all about using sets, but just couldn't come up with a set-based solution to this particular problem. If you tell me to go do some set-based operations, well, I'm all for it, if you can tell me how you'd code this particular problem.
Basically, I've got a number of stock items for which I need to make purchases. I want to make purchases based upon the cheapest available price, where I know the suppliers' prices and their stock levels. There's also a pack-size issue in here, wherein I want to buy by pack-size if possible.
I've already pulled a list of the things I need to purchase into #needorders, and suppliers' stock levels and prices into #orderedprices. Below I'm iterating through cursor CUR_NEEDED and creating a secondary cursor CUR_AVAILABLE:
DECLARE CUR_NEEDED CURSOR LOCAL SCROLL_LOCKS
FOR
SELECT GoodID
, ConditionID
, QuantityToShip
, OrderStatusID
, RetailerID
, PackSize
FROM #needorders
ORDER BY GoodID
, ConditionID
, PurchaseDate DESC
FOR UPDATE
OPEN CUR_NEEDED
FETCH NEXT FROM CUR_NEEDED INTO #GoodID, #ConditionID, #QuantityToShip, #OrderStatusID, #RetailerID, #PackSize
WHILE ##FETCH_STATUS = 0
BEGIN
DECLARE CUR_AVAILABLE CURSOR LOCAL SCROLL_LOCKS
FOR
SELECT SupplierStocklistItemID
, SupplierID
, StockLevel
, SupplierCurrencyID
, CostPrice
FROM #orderedprices
WHERE #orderedprices.GoodID = #GoodID
AND #orderedprices.ConditionID = #ConditionID
AND #orderedprices.StockLevel > 0
ORDER BY #orderedprices.PriceRank
FOR UPDATE
OPEN CUR_AVAILABLE
FETCH NEXT FROM CUR_AVAILABLE INTO #SupplierStocklistItemID, #SupplierID, #StockLevel, #SupplierCurrencyID, #CostPrice
WHILE ##FETCH_STATUS = 0
BEGIN
/*
Buy as many #PackSize as we need to cover how many we require, unless the supplier
only has a certain number, in which case buy that number.
E.g., need 14, pack size 5, 2 suppliers
Supplier A has 11
Supplier B has 40
Buy 9 from Supplier A, with our remaining need being 3.
Buy 5 from supplier B, with our remaining need being -2
*/
--feed rows into #supplierpurchasesbase while #StockLevel > 0
--Figure out how many we need to buy, based upon PackSize
IF #QuantityToShip % #PackSize > 0
BEGIN
SET #Buy = #QuantityToShip - #QuantityToShip % #PackSize + #PackSize
END
ELSE
BEGIN
SET #Buy = #QuantityToShip
END
IF #StockLevel < #Buy
BEGIN
--PRINT 'Supplier only has ' + CAST(#StockLevel AS VARCHAR) + ' for us to buy.'
SET #Buy = #StockLevel
END
INSERT INTO #supplierpurchasesbase (
GoodID
, ConditionID
, SupplierStocklistItemID
, Quantity
, SupplierID
, SupplierCurrencyID
, CostPrice
, RetailerID )
SELECT #GoodID
, #ConditionID
, #SupplierStocklistItemID
, #Buy
, #SupplierID
, #SupplierCurrencyID
, #CostPrice
, #RetailerID
--update #QuantityToShip & the row in CUR_AVAILABLE
IF #StockLevel <= #Buy
BEGIN
UPDATE CUR_AVAILABLE
SET StockLevel = #StockLevel - #Buy
WHERE CURRENT OF CUR_AVAILABLE
SET #QuantityToShip = 0
END
ELSE
BEGIN
UPDATE CUR_AVAILABLE
SET StockLevel = 0
WHERE CURRENT OF CUR_AVAILABLE
SET #QuantityToShip = #QuantityToShip - #Buy
END
--update the stocklevel so we don't see the thing again if we've used it up.
IF #QuantityToShip = 0 --Don't need any more
BEGIN
UPDATE CUR_NEEDED
SET OrderStatusID = #StatusPendingPO
WHERE CURRENT OF CUR_NEEDED
BREAK
END
ELSE --Need more, move next, if we can
FETCH NEXT FROM CUR_AVAILABLE INTO #SupplierStocklistItemID, #SupplierID, #StockLevel, #SupplierCurrencyID, #CostPrice
END
CLOSE CUR_AVAILABLE
DEALLOCATE CUR_AVAILABLE
FETCH NEXT FROM CUR_NEEDED INTO #GoodID, #ConditionID, #QuantityToShip, #OrderStatusID, #RetailerID, #PackSize
END
CLOSE CUR_NEEDED
DEALLOCATE CUR_NEEDED
The problem I'm running into is that I get I'm getting the error
Invalid object name 'CUR_AVAILABLE'.
when I'm attempting to update CURRENT OF CUR_AVAILABLE.
I've tried defining the CUR_AVAILABLE cursor as #CUR_AVAILABLE but get a different error. I've tried defining the CUR_AVAILABLE cursor outside of the WHILE loop of CUR_NEEDED, I've tried not closing / deallocating the cursor, etc. None of this seems to work.
Any ideas where I'm going wrong, here (other than not using sets, unless you've got a set-based solution)?
The following query uses a recursive CTE and, therefore, can't be considered a truly set-based solution. Nevertheless, I would still expect it to perform better than your two cursors (or to be worth trying, at the very least):
WITH buys (
GoodID,
ConditionID,
SupplierStocklistItemID,
Quantity,
SupplierID,
SupplierCurrencyID,
CostPrice,
RetailerID,
PriceRank,
RemainingNeed,
PackSize
)
AS (
SELECT
GoodID,
ConditionID,
SupplierStocklistItemID = 0,
Quantity = 0,
SupplierID = 0,
SupplierCurrencyID = 0,
CostPrice = CAST(0.00 AS decimal(10,2)),
RetailerID,
PriceRank = 0,
RemainingNeed = QuantityToShip,
PackSize
FROM #needorders
UNION ALL
SELECT
p.GoodID,
p.ConditionID,
p.SupplierStockListItemID,
Quantity = y.CurrentBuy,
p.SupplierID,
p.SupplierCurrencyID,
p.CostPrice,
b.RetailerID,
p.PriceRank,
RemainingNeed = b.RemainingNeed - y.CurrentBuy,
b.PackSize
FROM #orderedprices p
INNER JOIN buys b ON p.GoodID = b.GoodID
AND p.ConditionID = b.ConditionID
AND p.PriceRank = b.PriceRank + 1
CROSS APPLY (
SELECT RemainingNeedAdjusted =
(b.RemainingNeed + b.PackSize - 1) / b.PackSize * b.PackSize
) x
CROSS APPLY (
SELECT CurrentBuy = CASE
WHEN x.RemainingNeedAdjusted > p.StockLevel
THEN p.StockLevel
ELSE x.RemainingNeedAdjusted
END
) y
WHERE p.StockLevel > 0
AND b.RemainingNeed > 0
)
SELECT
GoodID,
ConditionID,
SupplierStocklistItemID,
Quantity,
SupplierID,
SupplierCurrencyID,
CostPrice,
RetailerID
FROM buys
WHERE PriceRank > 0
ORDER BY
GoodID,
ConditionID,
PriceRank
Basically, the CTE forms the rows almost identical to those your query is inserting into #supplierpurchasesbase, except it additionally features auxiliary columns serving as kind of internal variables. (They are not pulled by the final SELECT, though.)
The anchor part forms a set of 0-quantity records based on the #needordered table, together with the initial values for the auxiliary columns. The recursive part contains all the logic: calculates the quantity to buy, updates the "remaining need" quantity for the next iteration, checks whether the next iteration is needed.
Certain assumptions have been made, and I hope you'll be able find your way around them if they do not match your real situation. For instance, quantities, pack sizes are assumed to be integer, and part of the logic relies on that, because it uses integral division. It is also assumed that PriceRank is a sequence of integers starting from 1, unique per (GoodID, ConditionID).
This script, as well as a minimal test setup, can be found, tested, modified, and tested on SQL Fiddle.
The problem was twofold: The update syntax should not be:
UPDATE CUR_AVAILABLE
SET StockLevel = #StockLevel - #Buy
WHERE CURRENT OF CUR_AVAILABLE
Rather, the syntax should be:
UPDATE #orderedprices
SET StockLevel = #StockLevel - #Buy
WHERE CURRENT OF CUR_AVAILABLE
Also, in order to be updatable, the temp table needed to have a primary key:
ALTER TABLE #orderedprices ADD CONSTRAINT PRIMARY KEY CLUSTERED (RowCtr)
Lesson learned, I guess, but it certainly took me a fair bit of grief to find the solution!