How to grab last row in database table with specific requirements? - c#

Okay so I am accepting payments on my site (via Authorize.Net). The payment form redirects to a receipt page.
I will have a column in the database for an invoice code (column InvoiceCode), which is RRC0A in this instance. Then I will have another column for an 8 digit number (column InvoiceNumber). Then I will have InvoiceCode + InvoiceNumber = InvoiceId. For example, the InvoiceId will be RRC0A + 8 numbers. It will increment as such: 00000000, 00000001, 00000002, etc. Therefore the InvoiceId will be RRC0A00000001. I cannot simply increment the column in my database because there will be other InvoiceCodes that also start at 00000000.
I need to increment the InvoiceNumber by one when I add a new row. How can I grab the last InvoiceNumber that was entered into the database? It must be associated with the InvoiceCode RRC0A. This could occur when more than 1 person is making a payment, so I am not sure of the best way.
How can I pad the incrementing InvoiceNumber with 0's in front so that it is always 8 digits?

Using an identity and a computed column you can created you invoice numbers with the correct formatting at the time of insert.
CREATE TABLE [dbo].[Invoices](
[ID] [int] IDENTITY(1,1) NOT NULL,
[Code] [nchar](5) NOT NULL,
[InvoiceNumber] AS ([Code]+right('00000000'+CONVERT([nvarchar](10),[ID]),(8))) PERSISTED,
[Cost] [decimal](18, 2) NOT NULL,
CONSTRAINT [PK_Invoices] PRIMARY KEY CLUSTERED
(
[ID] ASC
)
)
sample bulk insert
INSERT INTO [dbo].[Invoices] ([Code], [Cost])
OUTPUT INSERTED.*
SELECT 'ABC01', 500 UNION ALL
SELECT 'ABC01', 501 UNION ALL
SELECT 'EFG23', 502 UNION ALL
SELECT 'RRAc1', 503 UNION ALL
SELECT 'ABC01', 504
output
ID Code InvoiceNumber Cost
1 ABC01 ABC0100000001 500.00
2 ABC01 ABC0100000002 501.00
3 EFG23 EFG2300000003 502.00
4 RRAc1 RRAc100000004 503.00
5 ABC01 ABC0100000005 504.00
When you insert your records you can get the ID and InvoiceNumber back at the same time.
The values are also persisted so they may be indexed as you would other columns.

SELECT InvoiceCode, MAX(InvoiceID)
FROM yourTable t
GROUP BY InvoiceCode
This should return the latest InvoiceID for each InvoiceCode, but you can add your own WHERE clause to filter it down
As for how to pad-left in sql, check out this answer.

A as in one column is just a bad design
Have composite PK
InvCode (varchar), InvInt (int)
declare #InvCode varchar(20) = 'RRC0A'
insert into invoice (InvCode, InvInt)
OUTPUT INSERTED.InvInt, INSERTED.InvCode
select #InvCode, isnull(max(InvInt),-1) + 1
from invoice
where InvCode = #InvCode;
The isnull will deal with the first one
A single statement is a transaction so I don't think two simultaneous could clobber
Even if they did the PK would be violated so the insert would fail
use a view or a computed column for the formatted invoice number
CREATE TABLE [dbo].[Invoice](
[InvCode] [varchar](10) NOT NULL,
[InvInt] [int] NOT NULL,
[Formatted] AS ([InvCode]+right('00000000'+CONVERT([nvarchar](10),[InvInt]),(8))),
CONSTRAINT [PK_Invoice] PRIMARY KEY CLUSTERED
(
[InvCode] ASC,
[InvInt] ASC
)

You can grab the last InvoiceNumber with a SELECT query.
You can pad the invoice number with the + sign to concatenate two strings, and then use RIGHT() to get the right-most 8 characters.

Related

C# / SQL Determine matching score based on properties

On a project where we have SQL tables called Products and Conditions, we want to determine which product belongs to which most matching condition, because a product can belong to multiple conditions.
Is there a way to do this in C# or SQL?
Below you can find a shorted version of the tables with the properties that we want to match on:
CREATE TABLE Products
(
[Id] INT NOT NULL PRIMARY KEY IDENTITY(1,1),
[Property1] SMALLINT NULL,
[Property2] SMALLINT NULL,
[Property3] NVARCHAR(20) NULL,
[Property4] NVARCHAR(20) NULL
)
CREATE TABLE Conditions
(
[Id] INT NOT NULL PRIMARY KEY IDENTITY(1,1),
[Property1] SMALLINT NULL,
[Property2] SMALLINT NULL,
[Property3] NVARCHAR(20) NULL,
[Property4] NVARCHAR(20) NULL
)
As a result we want for each product the conditions and sorted by most matching score based on the 4 properties.
Because we have 4 properties, the resulting score could be 0 / 25 / 50 / 75 / 100.
In sql you can join the two tables on matching properties and use iif method to compute the total score and order the results by the total score like below :
Select * from (
Select p.*, c.*,
iif([Property1] = p.[Property1],25,0) +
iif([Property2] = p.[Property2],25,0) +
iif([Property3] = p.[Property3],25,0) +
iif([Property4] = p.[Property4],25,0) [TotalScore]
from Products p inner join Conditions c
on c.[Property1] = p.[Property1] or
c.[Property2] = p.[Property2] or
c.[Property3] = p.[Property3] or
c.[Property4] = p.[Property4]) q
order by TotalScore desc

Sequential Increment in Alphanumeric Invoice Number using MSSQL

i have a table named as sales. In this table i have a column Invoice_Number. What i want to achieve is to get invoice number in alphanumeric sequential order i.e. one Static Letter and then a number that changes just like A1,A2,A3... and so on. i don't want to change A to B as i saw in rest of the examples.
I can get the numeric increment by this query:
Select IsNull(MAX([Invoice_Number] + 1), 1) from sales
But it cannot add an alphabet prior to it.
Thanks.
Try using a cast like so to return a varchar, let me know if this helps :).
SELECT 'A' + CAST(ISNULL(MAX([Invoice_Number] + 1), 1) AS nvarchar) FROM sales
You could use your existing identity column as a seed. Combine this with a computed column to return the InvoiceNumber.
-- Combine a seed with a computed column.
CREATE TABLE #Example
(
Seed INT IDENTITY(1,1),
InvoiceNumber AS 'A' + CAST(Seed AS VARCHAR(5)),
Customer VARCHAR(25)
);
-- Add some sample records.
INSERT INTO #Example
(
Customer
)
VALUES
('A PLC'),
('B PLC'),
('C LTD')
;
-- Returns InvoiceNumbers A1, A2 & A3.
SELECT
*
FROM
#Example
;

Translating a Set of Integers to String Values in SQL Server( Using T-SQL for certain and maybe .NET)

I have multiple tables that have int values in them that represent a specific string (Text) and I want to convert the integers to the string values. The goal is to make a duplicate copy of the table and then translate the integers to strings for easy analysis.
For example, I have the animalstable and the AnimalType Field consists of int values.
0 = "Cat", 1 = dog, 2= "bird", 3 = "turtle", 99 = "I Don't Know"
Can someone help me out with some starting code for this translation to animalsTable2 showing the string values?
Any help would be so very much appreciated! I want to thank you in advance for your help!
The best solution would be to create a related table that defines the integer values.
CREATE TABLE [Pets](
[ID] [int] NOT NULL,
[Pet] [varchar](50) NULL,
CONSTRAINT [PK_Pets] PRIMARY KEY CLUSTERED
([ID] ASC) ON [PRIMARY]
Then you can insert your pet descriptions but you can leave out the "I don't Know" item; it can be handled by left joining the Pets table to your main table.
--0 = "Cat", 1 = dog, 2= "bird", 3 = "turtle", 99 = "I Don't Know"`
INSERT INTO [Pets] ([ID],[Pet]) VALUES(0, 'cat');
INSERT INTO [Pets] ([ID],[Pet]) VALUES(1, 'dog');
INSERT INTO [Pets] ([ID],[Pet]) VALUES(2, 'bird');
INSERT INTO [Pets] ([ID],[Pet]) VALUES(3, 'turtle');
Now you can include the [Pets].[Descr] field in the output of your query like so
SELECT [MainTableFiled1]
,[MainTableFieldx]
,isnull([Pet], 'I dont know') as Pet
FROM [dbo].[MainTable] a
LEFT JOIN [dbo].[Pets] b
ON a.[MainTable_PetID] = b.[ID]
Alternatively, you can just define the strings in a case statement inside you query. This however, is not advised if you could be using the strings in more than one query.
Select case SomeField
when 0 then 'cat'
when 1 then 'dog'
when 2 then 'bird'
when 3 then 'turtle'
else 'i dont know' end as IntToString
from SomeTable
The benefit of the related table is you have only one place to maintain your string definitions and any edits would propagate to all queries, views or procedures that use it.
You can create a temp table to store the mappings. Then insert from a join between that table and the original table like so:
-- Create temp table
DECLARE #animalMapping TABLE(
animalType int NOT NULL,
animalName varchar(30) NOT NULL
);
-- Insert values into temp table
INSERT INTO #animalMapping (animalType, animalName)
VALUES (0, 'Cat'),
(1, 'Dog'),
(2, 'Bird'),
(3, 'Turtle'),
(99, 'I don''t know');
-- Insert into new table
INSERT INTO animalsTable2
SELECT id, <other fields from animalstable>,
#animalMapping.animalName
FROM animalstable
JOIN #animalMapping
ON animalstable.AnimalType = #animalMapping.animalType

Entity Framework, random query paging

This is what I want to achieve:
I want to query my db to return a list of entities
Randomize the list
Store the IDS of items received for future queries
Run a new query on the same table where the IDs are in the list that I have stored
Order by the list that I have stored.
I have managed to achieve step 1, 2, 3, 4 already but step 5 is difficult. Can anyone help me with a query like so:
SELECT *
FROM table_name
WHERE id IN (1,2,3,4....)
ORDER BY (1,2,3,4....)
Thanks in advance
Try
SELECT table_name.*
FROM crazy_sorted_table
LEFT JOIN
table_name ON crazy_sorted_table.ID=table_name.ID
A normal join (equi join) should do the trick , here is sample approach i tested:
/**crazyOrder filled 100 rows with random value from 1-250 in Id**/
CREATE TABLE [dbo].[crazyOrder] (
[Id] INT NOT NULL,
[Area] VARCHAR (50) NULL,
PRIMARY KEY CLUSTERED ([Id] ASC)
);
/**Normal order is filled with value from 1-100 sequentially in id**/
CREATE TABLE [dbo].[normalOrder] (
[Id] INT NOT NULL,
[Name] VARCHAR (50) NULL,
PRIMARY KEY CLUSTERED ([Id] ASC)
);
create table #tempOrder
(id int)
insert into #tempOrder
Select top 10 Id
from crazyOrder
order by NewID()
go
Select n.*
from normalOrder n
join #tempOrder t
on t.id = n.id
I was able to retrieve the rows in the same order as in the temp table (i used a data generator for the values)

Suggestion for a tag cloud algorithm

I have a MSSQL 2005 table:
[Companies](
[CompanyID] [int] IDENTITY(1,1) NOT NULL,
[Title] [nvarchar](128),
[Description] [nvarchar](256),
[Keywords] [nvarchar](256)
)
I want to generate a tag cloud for this companies. But I've saved all keywords in one column separated by commas. Any suggestions for how to generate tag cloud by most used keywords. There could be millions of companies approx ten keywords per company.
Thank you.
Step 1: separate the keywords into a proper relation (table).
CREATE TABLE Keywords (KeywordID int IDENTITY(1,1) NOT NULL
, Keyword NVARCHAR(256)
, constraint KeywordsPK primary key (KeywordID)
, constraint KeywordsUnique unique (Keyword));
Step 2: Map the many-to-many relation between companies and tags into a separate table, like all many-to-many relations:
CREATE TABLE CompanyKeywords (
CompanyID int not null
, KeywordID int not null
, constraint CompanyKeywords primary key (KeywordID, CompanyID)
, constraint CompanyKeyword_FK_Companies
foreign key (CompanyID)
references Companies(CompanyID)
, constraint CompanyKeyword_FK_Keywords
foreign key (KeywordID)
references Keywords (KeywordID));
Step 3: Use a simple GROUP BY query to generate the 'cloud' (by example taking the 'cloud' to mean the most common 100 tags):
with cte as (
SELECT TOP 100 KeywordID, count(*) as Count
FROM CompanyKeywords
group by KeywordID
order by count(*) desc)
select k.Keyword, c.Count
from cte c
join Keyword k on c.KeywordID = k.KeywordID;
Step 4: cache the result as it changes seldom and it computes expensively.
I'd much rather see your design normalized as suggested by Remus, but if you're at a point where you can't change your design...
You can use a parsing function (the example I'll use is taken from here), to parse your keywords and count them.
CREATE FUNCTION [dbo].[fnParseStringTSQL] (#string NVARCHAR(MAX),#separator NCHAR(1))
RETURNS #parsedString TABLE (string NVARCHAR(MAX))
AS
BEGIN
DECLARE #position int
SET #position = 1
SET #string = #string + #separator
WHILE charindex(#separator,#string,#position) <> 0
BEGIN
INSERT into #parsedString
SELECT substring(#string, #position, charindex(#separator,#string,#position) - #position)
SET #position = charindex(#separator,#string,#position) + 1
END
RETURN
END
go
create table MyTest (
id int identity,
keywords nvarchar(256)
)
insert into MyTest
(keywords)
select 'sql server,oracle,db2'
union
select 'sql server,oracle'
union
select 'sql server'
select k.string, COUNT(*) as count
from MyTest mt
cross apply dbo.fnParseStringTSQL(mt.keywords,',') k
group by k.string
order by count desc
drop function dbo.fnParseStringTSQL
drop table MyTest
Both Remus and Joe are correct but yes as what Joe said if you dont have a choice then you have to live with it. I think I can offer you an easy solution by using an XML Data Type. You can already easily view the parsed column by doing this query
WITH myCommonTblExp AS (
SELECT CompanyID,
CAST('<I>' + REPLACE(Keywords, ',', '</I><I>') + '</I>' AS XML) AS Keywords
FROM Companies
)
SELECT CompanyID, RTRIM(LTRIM(ExtractedCompanyCode.X.value('.', 'VARCHAR(256)'))) AS Keywords
FROM myCommonTblExp
CROSS APPLY Keywords.nodes('//I') ExtractedCompanyCode(X)
now knowing that you can do that, all you have to do is to group them and count, but you cannot group XML methods so my suggestion is create a view of the query above
CREATE VIEW [dbo].[DissectedKeywords]
AS
WITH myCommonTblExp AS (
SELECT
CAST('<I>' + REPLACE(Keywords, ',', '</I><I>') + '</I>' AS XML) AS Keywords
FROM Companies
)
SELECT RTRIM(LTRIM(ExtractedCompanyCode.X.value('.', 'VARCHAR(256)'))) AS Keywords
FROM myCommonTblExp
CROSS APPLY Keywords.nodes('//I') ExtractedCompanyCode(X)
GO
and perform your count on that view
SELECT Keywords, COUNT(*) AS KeyWordCount FROM DissectedKeywords
GROUP BY Keywords
ORDER BY Keywords
Anyways here is the full article -->http://anyrest.wordpress.com/2010/08/13/converting-parsing-delimited-string-column-in-sql-to-rows/

Categories