Auto increment considering Tenant id in existing table - sql-server

I have two tables Tenants and Inovices. Invoices have tenant_id column, and I need to add invoice_number incremental field by tentan_id to table Invoices.
My problem is identical to this Auto increment considering Tenant id
Actually, this solution works for me, but now I need to fill the invoice_number increment field of existing rows on table before apply the trigger in this solution.
Something like this but incrementing by tenant_id:
DECLARE #invoice_number INT
SET ##invoice_number = 0
UPDATE Invoices
SET #invoice_number = invoice_number = #invoice_number + 1
GO
How to loop and increment by each tenant?

You might use the following solution
Add a column invoice_seq in your Tenants table, so your Tenants table should look like Tenants(tenant_id,name,...,invoice_seq), so on creating the invoice, you do the following steps
Increment the invoice_seq in table InvoiceTenants
update the Invoice record by setting the invoice_number ='I' format(tenant_id,'000') + '-' + format(invoice_seq,'000000')
Here your invoice number will look like:
I001-000001 <-- tenant 1 and sequence 1
I001-000002
I001-000003 <-- tenant 1 and sequence 3
I002-000001 <-- tenant 2 and sequence 1
I003-000001 <-- tenant 3 and sequence 1
** sample code **
-- for the example below i will user #tenant = 1, the below code can be converted to stored procedure of code behind in your application
declare #invoice_seq int,
#tenant int = 1 -- here you can change the number to the way you wish
-- retrieve the last tenant +1
select #invoice_seq = invoice_seq+1 from Tenants where tenant_id=#tenant
-- add the invoice
insert into Invoices(tenant_id,invoice_number,... other columns....)
values (#tenant,'I' + format(#tenant,'000') + '-' + format(#invoice_seq,'000000'),... other columns...)
-- update the invoice_seq in the Tenants table
update Tenants set invoice_seq = #invoice_seq where tenant_id=#tenant
Hope this will help you

Related

Merging records in a table and update related tables

I have a main table containing users that are linked to various other tables. Sometimes there are duplicates in this main table due to bad imported data and I would like to merge them. See the following tables.
Table: Users
UserID Username FirstName LastName
1 Main John Doe
2 Duplicate John Doo
Table: Records1
RecordID RecordName CreatedUserID UpdatedUserID
1 Test record 1 1 2
2 Test record 2 2 null
3 Test record 3 2 null
CreatedUserID and UpdatedUserID are foreign columns of Users.UserID.
So currently if I want to merge user 1 and 2, I would do it with these SQL statements:
UPDATE Records1 SET UpdatedUserID = 1 WHERE UpdatedUserID = 2
UPDATE Records1 SET CreatedUserID = 1 WHERE CreatedUserID = 2
DELETE FROM Users WHERE UserID = 2
This is just a sample subset but in reality, there are a LOT of related records tables for which I have to add additional SQL-Update statements.
I know I'm probably pushing my luck here, but is there perhaps a way to accomplish the above (update all related tables in a batch and delete the "duplicate" record) rather than updating each foreign field and each related table manually. The users table is basically the base table that links to all other tables so to create individual statements for each table is rather cumbersome so if a shortcut is available, that would be great.
is this helpful.?
Create Table Users(Id int, UserName varchar(10),FirstName varchar(10), LastName Varchar(10))
Create Table Records1(RecordID int, RecordName varchar(20), CreatedUserID int, UpdatedUserID int)
INSERT INTO Users
SELECT 1,'Main','John','Doe' Union All
SELECT 2,'Duplicate','John','Doo' Union All
SELECT 3,'Main3','ABC','MPN' Union All
SELECT 4,'Duplicate','ABC','MPT'
Insert into Records1
SELECT 1,'Test record 1',1,2 Union All
SELECT 2,'Test record 2',2,null Union All
SELECT 3,'Test record 3',2,null Union All
SELECT 1,'Test record 1',3,4 Union All
SELECT 2,'Test record 2',4,null Union All
SELECT 3,'Test record 3',4,null
Select u1.Id as CreatedUserID,U2.id as UpdatedUserID
Into #tmpUsers
from Users u1
JOIN Users u2
--This Conidition Should be changed based on the criteria for identifying Duplicates
on u1.FirstName=u2.FirstName and U2.UserName='Duplicate'
Where u1.UserName<>'Duplicate'
Update r
Set r.UpdatedUserID=u.CreatedUserID
From Records1 r
JOIN #tmpUsers u on r.CreatedUserID=u.CreatedUserID
Update r
Set r.CreatedUserID=u.CreatedUserID
From Records1 r
JOIN #tmpUsers u on r.CreatedUserID=u.UpdatedUserID
Delete from Users Where UserName='Duplicate'
Select * from Users
Select * from Records1
Drop Table #tmpUsers
Since the process of identifying duplicate accounts will be manual then there will (generally) be pairs of accounts to be processed. (I'm assuming that the Inspector can't tick off 15 user accounts as duplicates in your UI and submit the whole lot for processsing.)
A stored procedure like the following may be a good start:
create procedure MergeUsers
#RetainedUserId Int, -- UserId that is being kept.
#VictimUserId Int -- UserId that is to be removed.
as
begin
-- Validate the input.
-- Optional, but you may want some reality checks.
-- (Usernames are probably unique already, eh?)
declare #UsernameMatch as Int, #FirstNameMatch as Int, #LastNameMatch as Int, #EmailMatch as Int;
select
#UsernameMatch = case when R.Username = V.Username then 1 else 0 end,
#FirstNameMatch = case when R.FirstName = V.FirstName then 1 else 0 end,
#LastNameMatch = case when R.LastName = V.LastName then 1 else 0 end,
#EmailMatch = case when R.Email= V.Emailthen 1 else 0 end
from Users as R inner join
Users as V on V.UserId = #VictimUserId and R.UserId = #RetainedUserId;
if #UsernameMatch + #FirstNameMatch + #LastNameMatch + #EmailMatch < 2
begin
-- The following message should be enhanced to provide a better clue as to which user
-- accounts are being processed and what did or didn't match.
RaIsError( 'MergeUsers: The two user accounts should have something in common.', 25, 42 );
return;
end;
-- Update all of the related tables.
-- Using a single pass through each table and updating all of the appropriate columns may improve performance.
-- The case expression will only alter the values which reference the victim user account.
update Records1
set
CreatedUserId = case when CreatedUserId = #VictimId then #RetainedUserId else CreatedUserId end,
UpdatedUserId = case when UpdatedUserId = #VictimId then #RetainedUserId else UpdatedUserId end
where CreatedUserId = #VictimUserId or UpdatedUserId = #VictimUserId;
update Records2
set ...
where ...;
-- Houseclean Users .
delete from Users
where UserId = #VictimUserId;
end;
NB: Left as an exercise is adding try/catch and a transaction in the SP to ensure that the merge is an all or nothing operation.

SQL Server check constraint logic

I've got a table that has such kind of structure:
CREATE TABLE #Mine
(
ProductID INT
, CountryID INT
, ApplicationID INT
);
Let's assume it has data as follows:
ProductID CountryID ApplicationID
1 2 -1
1 3 -1
1 3 2
I'd like to enforce such logic that there's no other ProductID/CountryID combination in entire table if it exists with ApplicationID = -1. In My example 2nd and 3rd row wouldn't pass this.
I could create a custom function to validate that and make a CHECK constraint out of it. Is there perhaps a more elegant way to do it?
I would split your task. First, assign unique constraint (this can be table Key):
CREATE UNIQUE INDEX IX_UQ ON Mine(ProductId, CountryId, ApplicationId)
This is for trivial validations and to improve trigger query.
Second, your check requires many records involved (no CHECK constraint possible). This is task for trigger:
CREATE TRIGGER trMine
ON Mine FOR INSERT,UPDATE
IF (EXISTS(
SELECT Mark FROM
(
SELECT MAX(CASE WHEN M.ApplicationId=-1 THEN 1 ELSE 0 END)*(COUNT(*)-1) Mark
FROM Mine M
JOIN inserted I ON M.ProductId=I.ProductId AND M.CountryId=I.CountryId
GROUP BY M.ProductId,M.CountryId
) Q
WHERE Mark != 0
)) THROW 50000, 'Validation error', 1
When there are 2 or more records (COUNT(*)-1>0) and there is any record with ApplicationId=-1, Mark evaluates to something != 0. This is your violation rule.
You can just use a Unique Filtered Index:
CREATE UNIQUE INDEX IX_UniqueNegativeApp ON Mine(ProductID, CountryID) WHERE ApplicationID = -1

Oracle update where select statement returns multiple row error

Hi i have a table like this:
ID Name Number
1 john 91234567
2 tom 98765432
3 ken 91357246
...
I am trying to change number [91234567] to another number but i get this single row query returns more than 1 row.
My statement:
Update table set number = '9000000' where id = (select id from table where number = '91234567')
Perhaps i have another record with the same number further down the table.
Since i do not have access to the id, how can i change my statement? Thanks.
Try to use IN instead of =
Update table
set number = '9000000'
where id IN (select id from table where number = '91234567')
If you need to change all the records containing 91234567 to 9000000:
update table
set number = 9000000
where number = 91234567

conditional "next value for sequence"

scenario:
Sql Server 2012 Table named "Test" has two fields. "CounterNo" and "Value" both integers.
There are 4 sequence objects defined named sq1, sq2, sq3, sq4
I want to do these on inserts:
if CounterNo = 1 then Value = next value for sq1
if CounterNo = 2 then Value = next value for sq2
if CounterNo = 3 then Value = next value for sq3
I think, create a custom function assign it as default value of Value field. But when i tried custom functions not supports "next value for Sequence Objects"
Another way is using trigger. That table has trigger already.
Using a Stored Procedure for Inserts is the best way. But EntityFramework 5 Code-First is not supporting it.
Can you suggest me a way to achieve this.
(if you show me how can i do it with custom functions you can also post it here. It's another question of me.)
Update:
In reality there are 23 fields in that table and also primary keys setted and i'm generating this counter value on software side, using "counter table".It is not good to generate counter values on client side.
I'm using 4 sequence objects as counters because they represents different types of records.
If i use 4 counters on same record at same time, all of them generates next values. I want only related counter generates it's next value while others remains same.
I'm not shure if I fully understand your use case but maybe the following sample illustrates what you need.
Create Table Vouchers (
Id uniqueidentifier Not Null Default NewId()
, Discriminator varchar(100) Not Null
, VoucherNumber int Null
-- ...
, MoreData nvarchar(100) Null
);
go
Create Sequence InvoiceSequence AS int Start With 1 Increment By 1;
Create Sequence OrderSequence AS int Start With 1 Increment By 1;
go
Create Trigger TR_Voucher_Insert_VoucherNumer On Vouchers After Insert As
If Exists (Select 1 From inserted Where Discriminator = 'Invoice')
Update v
Set VoucherNumber = Next Value For InvoiceSequence
From Vouchers v Inner Join inserted i On (v.Id = i.Id)
Where i.Discriminator = 'Invoice';
If Exists (Select 1 From inserted Where Discriminator = 'Order')
Update v
Set VoucherNumber = Next Value For OrderSequence
From Vouchers v Inner Join inserted i On (v.Id = i.Id)
Where i.Discriminator = 'Order';
go
Insert Into Vouchers (Discriminator, MoreData)
Values ('Invoice', 'Much')
, ('Invoice', 'More')
, ('Order', 'Data')
, ('Invoice', 'And')
, ('Order', 'Again')
;
go
Select * From Vouchers;
Now Invoice- and Order-Numbers will be incremented independently. And as you can have multiple insert triggers on the same table, that shouldn't be an issue.
I think you're thinking about this in the wrong way. You have 3 values and these values are determined by another column. Switch it around, create 3 columns and remove the Counter column.
If you have a table with value1, value2 and value3 then the Counter value is implied by the column in which the value resides. Create a unique index on these three columns and add an identity column for a primary key and you're sorted; you can do it all in a stored procedure easily.
If you have four different types of records, use four different tables, with a separate identity column in each one.
If you need to see all the data together, then use a view to combine them:
create v_AllTypes as
select * from type1 union all
select * from type2 union all
select * from type3 union all
select * from type4;
Alternatively, do the calculation of the sequence number on output:
select t.*,
row_number() over (partition by CounterNo order by t.id) as TypeSeqNum
from AllTypes t;
Something seems amiss with your data model if it requires conditional updates to four identity columns.

T-SQL Grouping Sets of Information

I have a problem which my limited SQL knowledge is keeping me from understanding.
First the problem:
I have a database which I need to run a report on, it contains configurations of a users entitlements. The report needs to show a distinct list of these configurations and a count against each one.
So a line in my DB looks like this:
USER_ID SALE_ITEM_ID SALE_ITEM_NAME PRODUCT_NAME CURRENT_LINK_NUM PRICE_SHEET_ID
37715 547 CultFREE CultPlus 0 561
the above line is one row of a users configuration, for every user ID there can be 1-5 of these lines. So the definition of a configuration is multiple rows of data sharing a common User ID with variable attributes..
I need to get a distinct list of these configurations across the whole table, leaving me just one configuration set for every instance where > 1 has that configuration and a count of instances of that configuration.
Hope this is clear?
Any ideas?!?!
I have tried various group by's and unions, also the grouping sets function to no avail.
Will be very greatful if anyone can give me some pointers!
Ouch that hurt ...
Ok so problem:
a row represents a configurable line
users may be linked to more than 1 row of configuration
configuration rows when grouped together form a configuration set
we want to figure out all of the distinct configuration sets
we want to know what users are using them.
Solution (its a bit messy but the idea is there, copy and paste in to SQL management studio) ...
-- ok so i imported the data to a table named SampleData ...
-- 1. import the data
-- 2. add a new column
-- 3. select all the values of the config in to the new column (Configuration_id)
--UPDATE [dbo].[SampleData]
--SET [Configuration_ID] = SALE_ITEM_ID + SALE_ITEM_NAME + [PRODUCT_NAME] + [CURRENT_LINK_NUM] + [PRICE_SHEET_ID] + [Configuration_ID]
-- 4. i then selected just the distinct values of those and found 6 distinct Configuration_id's
--SELECT DISTINCT [Configuration_ID] FROM [dbo].[SampleData]
-- 5. to make them a bit easier to read and work with i gave them int values instead
-- for me it was easy to do this manually but you might wanna do some trickery here to autonumber them or something
-- basic idea is to run the step 4 statement but select into a new table then add a new primary key column and set identity spec on it
-- that will generate u a bunch of incremental numbers for your config id's so u can then do something like ...
--UPDATE [dbo].[SampleData] sd
--SET Configuration_ID = (SELECT ID FROM TempConfigTable WHERE Config_ID = sd.Configuration_ID)
-- at this point you have all your existing rows with a unique ident for the values combined in each row.
-- so for example in my dataset i have several rows where only the user_id has changed but all look like this ...
--SALE_ITEM_ID SALE_ITEM_NAME PRODUCT_NAME CURRENT_LINK_NUM PRICE_SHEET_ID Configuration_ID
--54101 TravelFREE TravelPlus 0 56101 1
-- now you have a config id you can start to work on building sets up ...
-- each user is now matched with 1 or more config id
-- 6. we use a CTE (common table expression) to link the possibles (keeps the join small) ...
--WITH Temp (ConfigID)
--AS
--(
-- SELECT DISTINCT SD.Configuration_Id --SD2.Configuration_Id, SD3.Configuration_Id, SD4.Configuration_Id, SD5.Configuration_Id,
-- FROM [dbo].[SampleData] SD
--)
-- this extracts all the possible combinations using the CTE
-- on the basis of what you told me, max rows per user is 6, in the result set i have i only have 5 distinct configs
-- meaning i gain nothing by doing a 6th join.
-- cross joins basically give you every combination of unique values from the 2 tables but we joined back on the same table
-- so its every possible combination of Temp + Temp (ConfigID + ConfigID) ... per cross join so with 5 joins its every combination of
-- Temp + Temp + Temp + Temp + Temp .. good job temp only has 1 column with 5 values in it
-- 7. uncomment both this and the CTE above ... need to use them together
--SELECT DISTINCT T.ConfigID C1, T2.ConfigID C2, T3.ConfigID C3, T4.ConfigID C4, T5.ConfigID C5
--INTO [SETS]
--FROM Temp T
--CROSS JOIN Temp T2
--CROSS JOIN Temp T3
--CROSS JOIN Temp T4
--CROSS JOIN Temp T5
-- notice the INTO clause ... this dumps me out a new [SETS] table in my db
-- if i go add a primary key to this and set its ident spec i now have unique set id's
-- for each row in the table.
--SELECT *
--FROM [dbo].[SETS]
-- now here's where it gets interesting ... row 1 defines a set as being config id 1 and nothing else
-- row 2 defines set 2 as being config 1 and config 2 and nothing else ... and so on ...
-- the problem here of course is that 1,2,1,1,1 is technically the same set as 1,1,1,2,1 from our point of view
-- ok lets assign a set to each userid ...
-- 8. first we pull the distinct id's out ...
--SELECT DISTINCT USER_ID usr, null SetID
--INTO UserSets
--FROM SampleData
-- now we need to do bit a of operating on these that's a bit much for a single update or select so ...
-- 9. process findings in a loop
DECLARE #currentUser int
DECLARE #set int
-- while theres a userid not linked to a set
WHILE EXISTS(#currentUser = SELECT TOP 1 usr FROM UserSets WHERE SetId IS NULL)
BEGIN
-- figure out a set to link it to
SET #set = (
SELECT TOP 1 ID
FROM [SETS]
-- shouldn't really do this ... basically need to refactor in to a table variable then compare to that
-- that way the table lookup on ur main data is only 1 per User_id
WHERE C1 IN (SELECT DISTINCT Configuration_id FROM SampleData WHERE USER_ID = #currentUser)
AND C2 IN (SELECT DISTINCT Configuration_id FROM SampleData WHERE USER_ID = #currentUser)
AND C3 IN (SELECT DISTINCT Configuration_id FROM SampleData WHERE USER_ID = #currentUser)
AND C4 IN (SELECT DISTINCT Configuration_id FROM SampleData WHERE USER_ID = #currentUser)
AND C5 IN (SELECT DISTINCT Configuration_id FROM SampleData WHERE USER_ID = #currentUser)
)
-- hopefully that worked
IF(#set IS NOT NULL)
BEGIN
-- tell the usersets table
UPDATE UserSets SET SetId = #set WHERE usr = #currentUser
set #set = null
END
ELSE -- something went wrong ... set to 0 to prevent endless loop but any userid linked to set 0 is a problem u need to look at
UPDATE UserSets SET SetId = 0 WHERE usr = #currentUser
-- and round we go again ... until we are done
END
SELECT
USER_ID,
SALE_ITEM_ID, ETC...,
COUNT(*) WhateverYouWantToNameCount
FROM TableNAme
GROUP BY USER_ID

Resources