Update multiple rows SQL Server 2008 - sql-server

Good day, I'm trying to update my table. Because there was an error(s) in my website. first Please check my table. (Penilaian_Header)
IdPenilaian | KodePenilaian | Nip | PositionCode | Total
1613 ----- 1603405 P028 0
1618 ----- 1602999 P028 0
1641 PE0001568 603060 P040 35
1640 PE0001567 1411862 P007 35
as you can see. There are two rows that KodePenilaian empty. So is there any chance to fill it ? so the result will be like this.
IdPenilaian | KodePenilaian | Nip | PositionCode | Total
1613 PE0001570 1603405 P028 0
1618 PE0001569 1602999 P028 0
1641 PE0001568 603060 P040 35
1640 PE0001567 1411862 P007 35
This how i generate KodePenilaian
select case
when right(max(KodePenilaian),7) is null then 'PE0000001'
else ('PE' + RIGHT('0000000' + cast(right(max(KodePenilaian),7) + 1 as nvarchar),7))
end KodePenilaian from Penilaian_Header
and here is there result when i run it
KodePenilaian
PE0001569
Thanks, Sorry for my bad english.

Not really used to sql server 2008
Try something like this:
update Penilaian_Header pen
set pen.KodePenilaian =
(select case
when right(max(newKode.KodePenilaian),7) is null then 'PE0000001'
else ('PE' + RIGHT('0000000' + cast(right(max(newKode.KodePenilaian),7) + 1 as nvarchar),7))
end KodePenilaian
from Penilaian_Header newKode)
where pen.KodePenilain = NULL
if ----- is NULL in your table

You can try this way...
;WITH cte
AS (SELECT *,
MAX(CONVERT(int, REPLACE(KodePenilaian, 'PE000', ''))) OVER () AS MaxNum,
ROW_NUMBER() OVER (ORDER BY kodePenilaian) AS rn
FROM YourTable)
UPDATE cte SET KodePenilaian = concat('PE000', maxnum + rn)
WHERE KodePenilaian IS NULL
Your table
create table YourTable (
IdPenilaian int, KodePenilaian varchar(20), Nip int, PositionCode varchar(10), Total INT)
insert into YourTable
(IdPenilaian , KodePenilaian , Nip , PositionCode , Total) values
( 1613 , NULL , 1603405 ,'P028', 0 )
,( 1618 , NULL , 1602999 ,'P028', 0 )
,( 1641 , 'PE0001568' , 603060 ,'P040', 35 )
,( 1640 , 'PE0001567' , 1411862 ,'P007', 35 )

Related

Cannot increment values in a T-SQL CTE

I have a case where I need to write a CTE ( at least this seems like the best approach) . I have almost everything I need in place but one last issue. I am using a CTE to generate many millions of a records and then I will insert them into a table. The data itself is almost irrelevant except for three columns. 2 date time columns and one character column.
The idea behind the CTE is this. I want one datetime field called Start and one int field called DataValue. I will have a variable which is the count of records I want to aim for and then another variable which is the number of times I want to repeat the datetime value. I don't think I need to explain the software this data represents but basically I need to have 16 rows where the Start value is the same and then after the 16th run I want to then add 15 minutes and then repeat. Effectively there will be events in 15 minute intervals and I will need X number of rows per 15 minute interval to represent those events.
This is my code
Declare #tot as int;
Declare #inter as int;
Set #tot = 26
Set #inter = 3;
WITH mycte(DataValue,start) AS
(
SELECT 1 DataValue, cast('01/01/2011 00:00:00' as datetime) as start
UNION all
if DataValue % #inter = 0
SELECT
DataValue + 1,
cast(DateAdd(minute,15,start) as datetime)
else
select
DataValue + ,
start
FROM mycte
WHERE DataValue + 1 <= #tot)
select
m.start,
m.start,
m.Datavalue%#inter
from mycte as m
option (maxrecursion 0);
I'll change the select statement into an insert statement once I get it working but the m.DataValue%#inter will make it repeat integer when inserting so the only thing I need is to figure out how to make the start be the same 16 times in a row and then increment
It seems that I cannot have an IF statement in the CTE but I am not sure how to accomplish that but what I was going to do was basically say if the DataValue%16 was 0 then increase the value of start.
In the end I should hopefully have something like this where in this case I only repeat it 4 times
+-----------+-------------------+
| DateValue | start |
+-----------+-------------------+
| 1 | 01/01/01 00:00:00 |
| 2 | 01/01/01 00:00:00 |
| 3 | 01/01/01 00:00:00 |
| 4 | 01/01/01 00:00:00 |
| 5 | 01/01/01 00:15:00 |
| 6 | 01/01/01 00:15:00 |
| 7 | 01/01/01 00:15:00 |
| 8 | 01/01/01 00:15:00 |
Is there another way to accomplish this without conditional statements?
You can use case when as below:
Declare #tot as int;
Declare #inter as int;
Set #tot = 26
Set #inter = 3;
WITH mycte(DataValue,start) AS
(
SELECT 1 DataValue, cast('01/01/2011 00:00:00' as datetime) as start
UNION all
SELECT DataValue+1 [Datavalue],
case when (DataValue % #inter) = 0 then cast(DateAdd(minute,15,start) as datetime) else [start] end [start]
FROM mycte
WHERE (DataValue + 1) <= #tot)
select
m.DataValue,
m.[start]
from mycte as m
option (maxrecursion 0);
This will give the below result
DataValue Start
========= =============
1 2011-01-01 00:00:00.000
2 2011-01-01 00:00:00.000
3 2011-01-01 00:00:00.000
4 2011-01-01 00:15:00.000
5 2011-01-01 00:15:00.000
6 2011-01-01 00:15:00.000
7 2011-01-01 00:30:00.000
8 2011-01-01 00:30:00.000
9 2011-01-01 00:30:00.000
10 2011-01-01 00:45:00.000
11 2011-01-01 00:45:00.000
12 2011-01-01 00:45:00.000
....
26 2011-01-01 02:00:00.000
And if you dont want to use case when you can use double recursive cte as below:-
WITH mycte(DataValue,start) AS
( --this recursive cte will generate the same record the number of #inter
SELECT 1 DataValue, cast('01/01/2011 00:00:00' as datetime) as start
UNION all
SELECT DataValue+1 [DataValue],[start]
FROM mycte
WHERE (DataValue + 1) <= #inter)
,Increments as (
-- this recursive cte will do the 15 additions
select * from mycte
union all
select DataValue+#inter [DataValue]
,DateAdd(minute,15,[start]) [start]
from Increments
WHERE (DataValue + 1) <= #tot
)
select
m.DataValue,
m.[start]
from Increments as m
order by DataValue
option (maxrecursion 0);
it will give the same results.
You can do this with a tally table and some basic math. I'm not sure if your total rows are #tot or should they be #tot * #inter. If so, you just need to change the TOP clause. If you need more rows, you just need to alter the tally table generation.
Declare #tot as int;
Declare #inter as int;
Set #tot = 26
Set #inter = 3;
WITH
E(n) AS(
SELECT n FROM (VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0))E(n)
),
E2(n) AS(
SELECT a.n FROM E a, E b
),
E4(n) AS(
SELECT a.n FROM E2 a, E2 b
),
cteTally(n) AS(
SELECT TOP( #tot) ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) n
FROM E4
)
SELECT n, DATEADD( MI, 15* ((n-1)/#inter), '20110101')
FROM cteTally;

Logical lock in WHERE clause

Since FUNCCODE is shared between POSCODE and DEVCODE I can't call out both at the same time to eliminate the null values to insert the data into a separate table called JOINT. POSCODE and DEVCODE are FKs. I know there has to be a much easier way of doing this. I spent the last 2 weeks trying to craft a solution... It feels like I don't understand one thing to make this work. Any advice is appreciated.
Table setup
FUNCCODE | POSCODE | DEVCODE
11 1 NULL
12 NULL 1
13 2 NULL
14 NULL 2
The table needs to be rearranged and then inserted into a separate table called JOINT which is setup as:
POSCODE | POSFUNCCODE |DEVCODE | DEVFUNCCODE
1 11 1 12
2 13 2 14
Some of my attempts XD
Each join creates only 2 of the columns I need
SELECT
dbo.POSITION.POSCODE AS POSCODE,
dbo.FUNC.FUNCCODE AS POSFUNCCODE
FROM FUNC
INNER JOIN POSITION ON dbo.POSITION.POSCODE = dbo.FUNC.POSCODE
UNION ALL
SELECT
dbo.DEVICE.DEVCODE AS DEVCODE,
dbo.FUNC.FUNCCODE AS DEVFUNCCODE
FROM FUNC
INNER JOIN DEVICE ON dbo.DEVICE.DEVCODE = dbo.FUNC.DEVCODE
ORDER BY 1;
Only inserts the last row values
DECLARE #DATE DATETIME = GETDATE()
DECLARE #PC INT;
SELECT #PC = POSCODE
FROM func
WHERE poscode != 0
ORDER BY 1;
DECLARE #FCP INT;
SELECT #FCP = FUNCCODE
FROM FUNC
WHERE POSCODE != 0
ORDER BY 1;
DECLARE #DC INT;
SELECT #DC = devcode
FROM func
WHERE devcode != 0
ORDER BY 1;
DECLARE #FCD INT
SELECT #FCD = FUNCCODE
FROM FUNC
WHERE DEVCODE != 0
ORDER BY 1
INSERT INTO JOINT (POSCODE, POSFUNCCODE, DEVCODE, DEVFUNCCODE, JOINTTIME,
JOINTSTATUS)
VALUES (#PC, #FCP, #DC, #FCD, #DATE, 1)
If my understanding of your problem is correct, I think the below query would be a solution.
INSERT INTO JOINT (POSTCODE, DEVCODE, POSFUNCCODE, DEVFUNCCODE)
SELECT
POSTCODE,
DEVCODE,
MAX(CASE
WHEN POSCODE IS NOT NULL THEN FUNCCODE
ELSE NULL
END) POSFUNCCODE,
MAX(CASE
WHEN DEVCODE IS NOT NULL THEN FUNCCODE
ELSE NULL
END) DEVFUNCCODE
FROM FUNC
GROUP BY POSTCODE,DEVCODE
Second version after a better understanding
INSERT INTO JOINT (POSTCODE, DEVCODE, POSFUNCCODE, DEVFUNCCODE)
SELECT
t1.POSTCODE,
t2.DEVCODE,
t1.POSFUNCCODE,
t2.DEVFUNCCODE
(SELECT
POSTCODE,
MAX(CASE
WHEN POSCODE IS NOT NULL THEN FUNCCODE
ELSE NULL
END) POSFUNCCODE
FROM FUNC
GROUP BY POSTCODE) t1
INNER JOIN
(SELECT
DEVCODE,
MAX(CASE
WHEN DEVCODE IS NOT NULL THEN FUNCCODE
ELSE NULL
END) DEVFUNCCODE
FROM FUNC
GROUP BY DEVCODE) t2
ON t1.POSTCODE = t2.DEVCODE
Against my better judgment, I created a loop which inserted the proper values.... I know this is not the correct way to approach this, please excuse me since my experience in SQL is very little. But this corrects the interface issue in the client program. Shout out to #user4219031 for guidance!
DECLARE #DATE DATETIME = GETDATE()
DECLARE #PC INT = 0;
DECLARE #FCP INT = 12;
DECLARE #DC INT = 0;
DECLARE #FCD INT = 13
WHILE ( #PC < 739 )
BEGIN
INSERT INTO JOINT (POSCODE, POSFUNCCODE, DEVCODE, DEVFUNCCODE, JOINTTIME, JOINTSTATUS)
VALUES( #PC, #FCP, #DC, #FCD, #DATE, 1)
SET #PC = #PC + 1
SET #FCP = #FCP +2
SET #DC = #DC + 1
SET #FCD = #FCD + 2
END
EDIT: I changed my query to account for NULL POSCODE or DEVCODE.
SQL Fiddle
MS SQL Server 2017 Schema Setup:
CREATE TABLE t ( FUNCCODE int, POSCODE int, DEVCODE int ) ;
INSERT INTO t (FUNCCODE, POSCODE, DEVCODE)
VALUES
( 11, 1, NULL )
, ( 12, NULL, 1 )
, ( 13, 2, NULL )
, ( 14, NULL, 2 )
, ( 42, NULL, 1 )
, ( 77, NULL, 7 )
, ( 88, NULL, 8 )
, ( 99, 9, NULL )
;
Create New Table And Insert Records
CREATE TABLE ti ( POSCODE int, pos_FUNCCODE int, DEVCODE int, dev_FUNCCODE int ) ;
INSERT INTO ti ( POSCODE, pos_FUNCCODE, DEVCODE, dev_FUNCCODE)
SELECT t1.POSCODE
, t1.FUNCCODE AS pos_FUNCODE
, t2.DEVCODE
, t2.FUNCCODE AS dev_FUNCCODE
FROM t t1
FULL OUTER JOIN t t2 ON t1.POSCODE = t2.DEVCODE
WHERE t1.POSCODE IS NOT NULL OR t2.DEVCODE IS NOT NULL
;
What's In The New Table?:
SELECT * FROM ti
Results:
| POSCODE | pos_FUNCCODE | DEVCODE | dev_FUNCCODE |
|---------|--------------|---------|--------------|
| 1 | 11 | 1 | 12 |
| 1 | 11 | 1 | 42 |
| 2 | 13 | 2 | 14 |
| 9 | 99 | (null) | (null) |
| (null) | (null) | 7 | 77 |
| (null) | (null) | 8 | 88 |
==========ORIGINAL==========
SQL Fiddle
MS SQL Server 2017 Schema Setup:
CREATE TABLE t ( FUNCCODE int, POSCODE int, DEVCODE int ) ;
INSERT INTO t (FUNCCODE, POSCODE, DEVCODE)
VALUES
( 11, 1, NULL )
, ( 12, NULL, 1 )
, ( 13, 2, NULL )
, ( 14, NULL, 2 )
;
Create New Table And Insert Records
CREATE TABLE ti ( POSCODE int, pos_FUNCCODE int, DEVCODE int, dev_FUNCCODE int ) ;
INSERT INTO ti ( POSCODE, pos_FUNCCODE, DEVCODE, dev_FUNCCODE)
SELECT t1.POSCODE
, t1.FUNCCODE AS pos_FUNCODE
, t2.DEVCODE
, t2.FUNCCODE AS dev_FUNCCODE
FROM t t1
INNER JOIN t t2 ON t1.POSCODE = t2.DEVCODE
WHERE t1.POSCODE IS NOT NULL
;
What's In The New Table?:
SELECT * FROM ti
Results:
| POSCODE | pos_FUNCCODE | DEVCODE | dev_FUNCCODE |
|---------|--------------|---------|--------------|
| 1 | 11 | 1 | 12 |
| 2 | 13 | 2 | 14 |

Return a Date Is null as maximum value in t-sql

I have this table.
ID Date Value
___ ____ _____
3241 9/17/12 5
3241 9/16/12 100
3241 9/15/12 20
4355 9/16/12 12
4355 9/15/12 132
4355 9/14/12 4
1001 NULL 89
1001 9/16/12 125
5555 NULL 89
1234 9/16/12 45
2236 9/15/12 128
2236 9/14/12 323
2002 9/17/12 45
I would like to select the maximum date grouped by id and including NULL as maximum value that should be in the result to get something like that.
ID Date Value
___ ____ _____
3241 9/17/12 5
4355 9/16/12 12
1001 9/16/12 125
5555 NULL 89
1234 9/16/12 45
2236 9/15/12 128
2002 9/17/12 45
I found a solution but that not include NULL as maximum value
the solution by #bluefeet
Return value at max date for a particular id
SELECT t1.id,
t2.mxdate,
t1.value
FROM yourtable t1
INNER JOIN
( SELECT max(date) mxdate,
id
FROM yourtable
GROUP BY id) t2 ON t1.id = t2.id
AND t1.date = t2.mxdate
I also search for a solution to see how can we select NULL maximum value in t-sql so i found this solution by #Damien_The_Unbeliever How can I include null values in a MIN or MAX?
SELECT recordid,
MIN(startdate),
CASE
WHEN MAX(CASE
WHEN enddate IS NULL THEN 1
ELSE 0
END) = 0 THEN MAX(enddate)
END
FROM tmp
GROUP BY recordid
But i m stuck i don't know how to merge between this two solution to get what i want.
PS: I m using SQL SERVER 2008
You can use this
SELECT
ID
,[Date]
,[Value]
FROM(
SELECT
*
, ROW_NUMBER() OVER(PARTITION BY ID ORDER BY ISNULL([Date],'9999-12-31') DESC) AS Row#
FROM yourtable
) A WHERE Row# = 1

SQL Query time spent between certain value

I have a database for all temperatures the last 10 years.
Now I want to find all periods where the temperature was above ex. 15 degree.
Simplified example:
...
2015-05-10 12
2015-05-11 15 |
2015-05-12 16 |
2015-05-13 17 |
2015-05-14 16 |
2015-05-15 15 |
2015-05-16 12
2015-05-17 11
2015-05-18 15 |
2015-05-19 12
2015-05-20 18 |
...
Så now I want get all time periods like this:
Min Max
2015-05-11 2015-05-15
2015-05-18 2015-05-18
2015-05-20 2015-05-20
Any suggestion of how this query will look like ?
You could use CTE
CREATE TABLE #Date (DateT datetime, Value int )
INSERT INTO #Date
VALUES ('2015-05-10',12),
('2015-05-11',15),
('2015-05-12',16),
('2015-05-13',17),
('2015-05-14',16),
('2015-05-15',15),
('2015-05-16',12),
('2015-05-17',11),
('2015-05-18',15),
('2015-05-19',12),
('2015-05-20',18)
WITH t AS (
SELECT DateT d,ROW_NUMBER() OVER(ORDER BY DateT) i
FROM #Date
WHERE Value >= 15
GROUP BY DateT
)
SELECT MIN(d) as DataStart,MAX(d) as DataFinal, ROW_NUMBER() OVER(ORDER BY DATEDIFF(day,i,d)) as RN
FROM t
GROUP BY DATEDIFF(day,i,d)
RN column is optional you could use
SELECT MIN(d) as DataStart,MAX(d) as DataFinal
FROM t
GROUP BY DATEDIFF(day,i,d)
Here is a solution using a gaps and islands algorithm. It looks kind of bulky but it runs fast and scales great. It is also modular if you want to add a gap-allowed parameter and you can rewrite it to partition by some other columns and it still performs nicely.
Inspired by Peter Larssons post here: http://www.sqltopia.com/?page_id=83
WITH [theSource](Col1,Col2)
AS
(
SELECT Col1,Col2 FROM (VALUES
('2015-05-10',12),
('2015-05-11',15),
('2015-05-12',16),
('2015-05-13',17),
('2015-05-14',16),
('2015-05-15',15),
('2015-05-16',12),
('2015-05-17',11),
('2015-05-18',15),
('2015-05-19',12),
('2015-05-20',18)
) as x(Col1,Col2)
)
,filteredSource([Value])
AS
(
SELECT Col1 as [Value]
FROM theSource WHERE Col2 >= 15
)
,cteSource(RangeStart, RangeEnd)
AS (
SELECT RangeStart,
CASE WHEN [RangeStart] = [RangeEnd] THEN [RangeEnd] ELSE LEAD([RangeEnd]) OVER (ORDER BY Value) END AS [RangeEnd]
FROM (
SELECT [Value],
CASE
WHEN DATEADD(DAY,1,LAG([Value]) OVER (ORDER BY [Value])) >= [Value] THEN NULL
ELSE [Value]
END AS RangeStart,
CASE
WHEN DATEADD(DAY,-1,LEAD([Value]) OVER (ORDER BY [Value])) <= [Value] THEN NULL
ELSE [Value]
END AS RangeEnd
FROM filteredSource
) AS d
WHERE RangeStart IS NOT NULL
OR RangeEnd IS NOT NULL
)
SELECT RangeStart AS [Min],
RangeEnd AS [Max]
FROM cteSource
WHERE RangeStart IS NOT NULL;

SQL Server : SELECT and CASE

I am trying to select data based on some parameters passed to my stored procedure. I have problems with the age, I am trying to do something like this:
If my stored procedure parameter #Age = 1 then I select age between 15 to 18, #Age = 2 then 19 - 25..., apparently this is incorrect, anyone can help. Thanks.:
SELECT
User
FROM
[Member] m
WHERE
((m.Gender = #Gender) or #Gender IS NULL)
and ((DATEDIFF(hour,m.DOB,GETDATE())/8766) Between
CASE
WHEN #Age = 1 THEN (SELECT DATEDIFF(hour, m.DOB, GETDATE())/8766 WHERE (SELECT DATEDIFF(hour, m.DOB, GETDATE())/8766) between 15 and 18)
WHEN #Age = 2 THEN (SELECT DATEDIFF(hour,m.DOB,GETDATE())/8766 WHERE (SELECT DATEDIFF(hour,m.DOB,GETDATE())/8766) between 19 and 25)
END)
I think this is what you are after (probably with some superfluous parenthesis):
Select
[User]
From
[Member] m
Where (
(m.Gender = #Gender) or
#Gender Is Null
) And (
(#Age = 1 And DateDiff(hour, m.Dob, GetDate())/8766 Between 15 and 18) Or
(#Age = 2 And DateDiff(hour, m.Dob, GetDate())/8766 Between 19 and 25)
)
If you've got a lot of clauses, it might be easier to read as (assuming a MemberID Primary Key)
Select
[User]
From
[Member] m
Inner Join (
Select
MemberID,
DateDiff(hour, m.Dob, GetDate())/8766 As Years
From
[Member]
) As y
On m.MemberID = y.MemberID
Where (
(m.Gender = #Gender) or
#Gender Is Null
) And (
(#Age = 1 And y.Year Between 15 and 18) Or
(#Age = 2 And y.Year Between 19 and 25)
)
Even better, you could add the ranges to a separate table called AgeRanges
+-------+------------+----------+
| AgeID | StartYears | EndYears |
+-------+------------+----------+
| 1 | 15 | 18 |
| 2 | 19 | 25 |
| ... | ... | ... |
+-------+------------+----------+
Select
[User]
From
[Member] m
Inner Join
[AgeRanges] a
On DateDiff(hour, m.Dob, GetDate())/8766 Between a.StartYears and a.EndYears And
a.AgeID = #Age
You could also make DateDiff(hour, m.Dob, GetDate())/8766 a computed column on your members table to simplify things (and make indexing possible if performance became an issue).

Resources