I have a temporary table with one column containing 3 pieces of data: Code, Date, and Quantity.
Example:
-------
FR123456
24/02/1988
500
I need to extract the data in this column into separate columns.
Example:
Code | Date | Quantity
--------- ----------- ----
FR123456 | 24/02/1988 | 500
I used this code:
SELECT [1], [2], [3]
FROM
(
SELECT row_number() OVER (ORDER BY splitdata DESC) AS Id, splitdata
FROM splitdata
) AS SourceTable
PIVOT
(
MIN (splitdata)
FOR id IN ([1], [2], [3])
) AS PivotTable;
The problem with it is that once the content of data changes, I may get the quantity content into the date column due to the aggregate function (MIN).
I assume this is SQL-Server related...
Your problem is: A SQL-Server table does not have any kind of implicit sort order. A simple SELECT * FROM SomeWhere can return in the sort order you've inserted your data, but can return completely different as well. The only chance to ensure a sort order is an ORDER BY at the outer-most query against a (set of) unique column(s).
You can create a sort order by kind of analysing your data:
This is a mockup-table with your test data:
DECLARE #mockup TABLE(YourColumn VARCHAR(100));
INSERT INTO #mockup VALUES
('FR123456')
,('24/02/1988')
,('500');
--The query will check the values if they can be casted to a number, to a date or not.
--This will be used to place a sort order to the values.
--I use 105 in CONVERT to enforce the dateformat dd-MM-yyyy
SELECT CASE WHEN TRY_CONVERT(DATE,YourColumn,105) IS NOT NULL THEN 2
ELSE CASE WHEN TRY_CAST(YourColumn AS INT) IS NOT NULL THEN 3 ELSE 1
END
END AS SortOrder
,YourColumn
FROM #mockup
ORDER BY SortOrder;
But if there are several triplets in your table, not just one as in your sample, I'm afraid you're lost...
Btw: Your own approach tries to do exactly the same:
SELECT row_number() OVER (order by splitdata desc)as Id
This will create kind of a sort order number, but it will be random (a quantity will appeare before or after the date depending on alphanumerical rules).
Hint
Add an IDENTITY column to your table. This will use an increasing number for any row the moment it is created. These values can be used to enforce the order as inserted (by using ORDER BY with this column).
UPDATE: Your query
SELECT [1], [2] , [3]
FROM
(SELECT CASE WHEN TRY_CONVERT(DATE,splitdata,105) IS NOT NULL THEN 2
ELSE CASE WHEN TRY_CAST(splitdata AS INT) IS NOT NULL THEN 3 ELSE 1
END
END AS Id , splitdata
from #mockup ) AS SourceTable
PIVOT
(
MIN (splitdata)
FOR id IN ([1], [2], [3])
) AS PivotTable;
Related
i have this table
series
id
1975
1
1985
1
1995
2
2000
2
what we trying to achieve is we add alphabet increment where id is same, example series will be 1975A, 1985B, and then 1995A 2000B when id is same, it is possible to do that? in query or in c# mvc code?
You can use below query.
At first,
I have added row number par group/ partition
Then as we know CHR(65)='A', Thus I have added 64 + auto row_rumber
And Convert to CHAR.
Add result with series field.
SELECT
CONCAT
(
series ,
CHR(64 + CAST ( row_number() OVER(PARTITION BY id order by id) AS integer ))
)
,id from table1
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
;
I have the following data:
[Receipt] [Date] [Barcode] [Quantity] [Supplier] [DeliveryID]
0001 01/01/2000 0000001 5 Grocery NULL
0001 01/01/2000 0000002 7 Grocery NULL
0001 01/01/2000 0000571 2 Grocery NULL
0002 01/01/2000 0000041 5 Grocey NULL
0002 01/01/2000 0000701 10 Grocey NULL
0003 01/01/2000 0000001 9 Groceri NULL
What can I do to put an incrementing value to the DeliveryID?
As you can see, there is nothing unique that can be used to distinguish a row from another row. There is a large chance that a similar data like any of those rows may be entered again.
Unfortunately, I cannot delete the table and create a new one for the sake of the new column.
I tried counting all the null rows first,
SELECT COUNT(*) as TotalCount FROM dbo.Delivery WHERE DeliveryID IS NULL
And create a for loop to update.
But I realized that after the first loop, all null will be replaced by 1, instead of updating each row per loop.
I have thought of combining the Receipt and Supplier to become a unique value, but as I said earlier, there's a chance that a similar data may be entered, thus creating a duplicate row, losing the uniqueness of the row
Use a row_number
with D as
(
select Receipt, Date, Barcode, DeliveryID, row_number() over(order by receipt , Date, Barcode) as rn
from delivery
)
update D
set DeliveryID = rn
where DeliveryID is null
You can even partition by receipt to provide a per-line within the receipt group:
with D as
(
select Receipt, Date, Barcode, DeliveryID, row_number() over(partition by receipt order by Date, Barcode) as rn
from delivery
)
update D
set DeliveryID = rn
where DeliveryID is null
You could use these columns:
[Receipt] [Date] [Barcode] [Quantity]
as a PrimaryKey so you can identify the rows you need.
Or for the column [Delivery ID] you could use the IDENTITY function:
https://blog.sqlauthority.com/2013/07/27/sql-server-how-to-an-add-identity-column-to-table-in-sql-server/
http://www.dailyfreecode.com/code/identity-function-439.aspx
Add column with identity that would help you
like
alter table tableName add DeliveryID int Identity(1,1)
Finding a solution to an issue in my project
I have stages associated with contracts. That is, a contract can be in either Active stage, Process stage or Terminated stage.
I need to get the no the days the contract was in each stage.
For example, if a contract C1 was in Active stage from 20/10/2013 to 22/10/2013, then in the Process stage from 22/10/2013 to 25/10/2013 and finally in Terminated stage from 25/10/2013 to 26/10/2013 and then again in Active from 26/10/2013 to 28/10/2013, then I should get as result
Active = 4days
Process = 3days
Terminated = 1day /likewise something
My table is created with these columns:
EntryId (primary key)
StageId (foreign key to Stage table)
ContractId (foreign key to contract table)
DateofStageChange
How to do this in SQL Server?
As asked pls find the table entries:
EntryID | Stage ID | Contract ID | DateChange
1 | A1 | C1 |20/10/2013
2 | P1 | C1 |22/10/2013
3 | T1 | C1 |25/10/2013
4 | A1 | C1 |26/10/2013
5 | P1 | C1 |28/10/2013
6 | T1 | C1 |Null(currently in this stage)
Need to use group by on Stage ID
it is important to check and make sure how data is populated in your table.Based on just your sample data and also note that if your entryid is not in sequence then you can create one sequence using row_number.
declare #t table(EntryId int identity(1,1), StageId int,ContractId varchar(10),DateofStageChange date)
insert into #t values
(1,'C1','2013-10-20'),(1,'C1','2013-10-22'),(2,'C1','2013-10-22'),(2,'C1','2013-10-25')
,(3,'C1','2013-10-25'),(3,'C1','2013-10-26'),(1,'C1','2013-10-26'),(1,'C1','2013-10-28')
Select StageId,sum([noOfDays]) [totalNofDays] from
(select a.StageId,a.ContractId,a.DateofStageChange [Fromdate],b.DateofStageChange [ToDate]
,datediff(day,a.DateofStageChange,b.DateofStageChange) [noOfDays]
from #t a
inner join #t b on a.StageId=b.StageId and b.EntryId-a.EntryId=1)t4
group by StageId
You can't with your current structure.
You can get the latest one by doing datediff(d, getdate(), DateOfStageChange)
but you don't have any history so you can't get previous status
This can be done in SQL with CTE.
You didnt provide your tablenames, so you'll need to change where I've indicated below, but it would look like this:
;WITH cte
AS (
SELECT
DateofStageChange, StageID, ContractID,
ROW_NUMBER() OVER (ORDER BY ContractID, StageId, DateofStageChange) AS RowNum
FROM
DateOfStageChangeTable //<==== Change this table name
)
SELECT
a.ContractId,
a.StageId,
Coalesce(sum(DATEDIFF(d ,b.DateofStageChange,a.DateofStageChange)), 'CurrentState`) as Days
FROM
cte AS A
LEFT OUTER JOIN
cte AS B
ON A.RowNum = B.RowNum + 1 and a.StageId = b.StageId and a.ContractId = b.ContractId
group by a.StageId, a.ContractId
This really is just a self join that creates a row number on a table, orders the table by StageID and date and then joins to itself. The first date on the first row of the stage id and date, joins to the second date on the second row, then the daterange is calculated in days.
This assumes that you only have 2 dates for each stage, if you have several, you would just need to do a min and max on the cte table.
EDIT:
Based on your sample data, the above query should work well. Let me know if you get any syntax errors and I'll fix them.
I added a coalesce to indicate the state they are currently in.
In essence I want to pick the best match of a prefix from the "Rate" table based on the TelephoneNumber field in the "Call" table. Given the example data below, '0123456789' would best match the prefix '012' whilst '0100000000' would best match the prefix '01'.
I've included some DML with some more examples of correct matches in the SQL comments.
There will be circa 70,000 rows in the rate table and the call table will have around 20 million rows. But there will be a restriction on the Select from the Call table based on a dateTime column so actually the query will only need to run over 0.5 million call rows.
The prefix in the Rate table can be up to 16 characters long.
I have no idea how to approach this in SQL, I'm currently thinking of writing a C# SQLCLR function to do it. Has anyone done anything similar? I'd appreciate any advice you have.
Example Data
Call table:
Id TelephoneNumber
1 0123456789
2 0100000000
3 0200000000
4 0780000000
5 0784000000
6 0987654321
Rate table:
Prefix Scale
1
01 1.1
012 1.2
02 2
078 3
0784 3.1
DML
create table Rate
(
Prefix nvarchar(16) not null,
Scale float not null
)
create table [Call]
(
Id bigint not null,
TelephoneNumber nvarchar(16) not null
)
insert into Rate (Prefix, Scale) values ('', 1)
insert into Rate (Prefix, Scale) values ('01', 1.1)
insert into Rate (Prefix, Scale) values ('012', 1.2)
insert into Rate (Prefix, Scale) values ('02', 2)
insert into Rate (Prefix, Scale) values ('078', 3)
insert into Rate (Prefix, Scale) values ('0784', 3.1)
insert into [Call] (Id, TelephoneNumber) values (1, '0123456789') --match 1.2
insert into [Call] (Id, TelephoneNumber) values (2, '0100000000') --match 1.1
insert into [Call] (Id, TelephoneNumber) values (3, '0200000000') --match 2
insert into [Call] (Id, TelephoneNumber) values (4, '0780000000') --match 3
insert into [Call] (Id, TelephoneNumber) values (5, '0784000000') --match 3.1
insert into [Call] (Id, TelephoneNumber) values (6, '0987654321') --match 1
Note: The last one '0987654321' matches the blank string because there are no better matches.
Since this is based on partial matching, a subselect would be the only viable option (unless, like LukeH assumes, every call is unique)
select
c.Id,
c.TelephoneNumber,
(select top 1
Scale
from Rate r
where c.TelephoneNumber like r.Prefix + '%' order by Scale desc
) as Scale
from Call c
SELECT t.Id, t.TelephoneNumber, t.Prefix, t.Scale
FROM
(
SELECT *, ROW_NUMBER() OVER
(
PARTITION BY c.TelephoneNumber
ORDER BY r.Scale DESC
) AS RowNumber
FROM [call] AS c
INNER JOIN [rate] AS r
ON c.TelephoneNumber LIKE r.Prefix + '%'
) AS t
WHERE t.RowNumber = 1
ORDER BY t.Id
Try this one:
select Prefix, min(c.TelephoneNumber)
from Rate r
left outer join Call c on c.TelephoneNumber like left(Prefix + '0000000000', 10)
or c.TelephoneNumber like Prefix + '%'
group by Prefix
You can use a left join to try to find a "better" match, and then eliminate such matches in your where clause. e.g.:
select
*
from
Call c
inner join
Rate r
on
r.Prefix = SUBSTRING(c.TelephoneNumber,1,LEN(r.Prefix))
left join
Rate r_anti
on
r_anti.Prefix = SUBSTRING(c.TelephoneNumber,1,LEN(r_anti.Prefix)) and
LEN(r_anti.Prefix) > LEN(r.Prefix)
where
r_anti.Prefix is null