How to update a field's value by an incremental value without using loop in SQL Server 2008? - sql

I have a table #temp in SQL Server 2008 like this:
Id p_id h_no f_id
------------------
1 100 A01 null
2 200 A02 null
3 300 A02 null
4 400 null null
5 500 null null
6 600 A03 null
7 700 A01 null
8 400 null null
So basically, every record has a p_id, but may or may not have h_no.
What I want is to replace f_id values with a dummy incremental number based on:
if h_no value of a record matches another(s), this (those) ones will have same f_id (check ids:1 & 7 or ids:2 & 3 in the example)
if h_no is null but p_id values are equal for some cases, they will have same f_id (check ids: 4 & 8 in the example)
For example, the sample table above should be:
Id p_id h_no f_id
-----------------
1 100 A01 1
2 200 A02 2
3 300 A02 2
4 400 null 3
5 500 null 4
6 600 A03 5
7 700 A01 1
8 400 null 3
I do not want to use a loop for this process. I am trying to find a more optimal solution for this. I need a query something like below, could not find the correct syntax.
declare #tempFID int = 1;
update t
set t.f_id = #tempFID++ --syntax error
from #temp t
inner join #temp t2 on t.Id = t2.Id
where (t.h_no is not null and t.h_no = t2.h_no)
or (t.h_no is null and t.p_id = t2.p_id)
I also tried but had syntax error:
update t
set t.f_id = (set #tempFID = #tempFID + 1) --syntax error
...
Any help would be so appreciated!

;WITH cte AS (
SELECT *
,CASE WHEN h_no IS NULL THEN p_id ELSE MIN(p_id) OVER (PARTITION BY h_no) END as PIdGroup
FROM
#Table
)
, cteFIdValue AS (
SELECT
Id
,DENSE_RANK() OVER (ORDER BY PIdGroup) as f_id
FROM
cte
)
UPDATE t
SET f_id = u.f_id
FROM
Table t
INNER JOIN cteFIdValue u
ON t.ID = u.ID
Find the minimum p_id for each h_no and just leave it as the assigned p_id if h_no is null
Then create a dense rank on the PidGroup
Update the Table
so you have problems besides a syntax error in your code above. First your join will only get the exact same record, you would have to change to t.ID <> t2.ID as left join and still need some sort of ranking. honestly I am not positive what you are attempting there.

This approach might be simpler:
update #temp
set f_id = isnull(f_id, 0) +
case when condition1 is met then value 1
etc
when final condition is met then 0
else null
end

Related

Set Insert trigger to store max value in another column

This is what my table looks like:
student_id
subject_id
total
max
101
1
6
102
2
5
103
1
9
101
1
10
103
2
2
104
1
7
I want the "max" column to be automatically populated when the total is inserted.
Expected Output:
student_id
subject_id
total
max
101
1
10
10
102
2
5
7
103
1
9
10
101
1
8
10
103
2
2
7
104
1
7
10
I will like to create a trigger for this.
This is my SELECT statement which works fine but how do I put it in a trigger?
WITH CTE AS (SELECT `subject_id`,MAX(`total`) AS MaxTotal
FROM results
GROUP BY `subject_id`
)
SELECT results.*,CTE.MaxTotal
FROM results
JOIN CTE ON results.`subject_id` = CTE.`subject_id`;
I did this but I got a plethora of errors
CREATE TRIGGER `max_score_before_INSERT` BEFORE INSERT ON `results`
FOR EACH ROW
SET NEW.max = (WITH CTE AS (SELECT `subject_id`,MAX(`NEW.total`) AS MaxTotal
FROM results
GROUP BY `subject_id`
)
SELECT results.*,CTE.MaxTotal
FROM results
JOIN CTE ON results.`subject_id` = CTE.`subject_id`
);
You can't reference "new" inside a BEFORE trigger type. You may want to first
insert the row, then update the column with an AFTER trigger type:
CREATE TRIGGER `max_score_before_INSERT` AFTER INSERT ON `results`
FOR EACH ROW
UPDATE <your_table_name>
INNER JOIN (SELECT subject_id,
MAX(total) AS total_max
FROM <your_table_name>
GROUP BY subject_id) cte
ON <your_table_name>.subject_id = cte.subject_id
SET <your_table_name>.max = cte.total_max;
Note that this approach will update the previous columns too, as if you insert a new field that becomes the max, you may want to update the already existing rows too. If not, then you can use a condition inside the UPDATE statement:
CREATE TRIGGER `max_score_before_INSERT` AFTER INSERT ON `results`
FOR EACH ROW
UPDATE <your_table_name>
INNER JOIN (SELECT subject_id,
MAX(total) AS total_max
FROM <your_table_name>
GROUP BY subject_id) cte
ON <your_table_name>.subject_id = cte.subject_id
SET <your_table_name>.max = cte.total_max;
WHERE <your_table_name>.student_id = NEW.student_id
AND <your_table_name>.subject_id = NEW.subject_id;

Nested sum loop until foreign key 'dies out'

I am pulling my hair out over a data retrieval function I'm trying to write. In essence this query is meant to SUM up the count of all voorwerpnummers in the Voorwerp_in_Rubriek table, grouped by their rubrieknummer gathered from Rubriek.
After that I want to keep looping through the sum in order to get to their 'top level parent'. Rubriek has a foreign key reference to itself with a 'hoofdrubriek', this would be easier seen as it's parent in a category tree.
This also means they can be nested. A value of 'NULL' in the hoofdcategory column means that it is a top-level parent. The idea behind this query is to SUM up the count of voorwerpnummers in Voorwerp_in_rubriek, and add them together until they are at their 'top level parent'.
As the database and testdata is quite massive I've decided not to add direct code to this question but a link to a dbfiddle instead so there's more structure.
https://dbfiddle.uk/?rdbms=sqlserver_2017&fiddle=8068a52da6a29afffe6dc793398f0998
I got it working in some degree using this query:
SELECT R2.hoofdrubriek ,
COUNT(Vr.rubrieknummer) AS aantal
FROM Rubriek R1
RIGHT OUTER JOIN Rubriek R2 ON R1.rubrieknummer = R2.hoofdrubriek
INNER JOIN Voorwerp_in_rubriek Vr ON R2.rubrieknummer = Vr.rubrieknummer
WHERE NOT EXISTS ( SELECT *
FROM Rubriek
WHERE hoofdrubriek = R2.rubrieknummer )
AND R1.hoofdrubriek IS NOT NULL
GROUP BY Vr.rubrieknummer ,
R2.hoofdrubriek
But that doesn't get back all items and flops in general. I hope someone can help me.
If I got it right
declare #t table (
rubrieknummer int,
cnt int);
INSERT #t(rubrieknummer, cnt)
SELECT R.rubrieknummer, COUNT(Vr.voorwerpnummer)
FROM Rubriek R
INNER JOIN voorwerp_in_rubriek Vr ON R.rubrieknummer = Vr.rubrieknummer
GROUP BY Vr.rubrieknummer, R.rubrieknummer;
--select * from #t;
with t as(
select rubrieknummer, cnt
from #t
union all
select r.hoofdrubriek, cnt
from t
join Rubriek r on t.rubrieknummer = r.rubrieknummer
)
select rubrieknummer, sum(cnt) cnt
from t
group by rubrieknummer;
applying to your fiddle data returns
rubrieknummer cnt
<null> 42
100 42
101 26
102 6
103 10
10000 8
10100 4
10101 1
10102 3
10500 4
10501 2
10502 2
15000 18
15100 6
15101 2
15102 2
15103 2
15500 12
15501 4
15502 3
15503 5
20000 6
20001 2
20002 1
20003 1
20004 2
25000 4
25001 1
25002 1
25003 1
25004 1
30001 2
30002 1
30004 3

update unique id to table base on phone or email address in sql server 2008 r2

currently i have following table structure in sql server2008 r2
tbusertable
userid username uid status
1 abc null null
2 yax null null
3 xcd null null
4 max null null
5 wax null null
6 ear null null
7 yes null null
8 sqt null null
9 ora null null
tbphtable
pid userid phnos
1 1 456
2 2 456
3 3 4568
4 4 789
5 5 5555
6 6 4599
7 7 456
8 8 111
9 9 111
tbeidtable
eid userid eid
1 1 y#gmail.com
2 2 abd#gmail.com
3 3 erer#gmail.com
4 4 yer#gmail.com
5 5 g#gmail.com
6 6 g#gmail.com
i want to update uid column of tbusertable table with unique id, if they have same phnos for eid without cursor because table have large records and cursor take long time to run
desire output
userid 1,2,7 have same phnos so they have same unique id and similarly
userid 5,6 have same eid so they have different unique id
and similarly userid 8,9 have same phnos so they have same different unique id
tbusertable
userid username uid status
1 abc D7CCBC4E-EEE6-4AC8-806D-A04DCC77DF54 null
2 yax D7CCBC4E-EEE6-4AC8-806D-A04DCC77DF54 null
3 xcd null null
4 max 0608CFF7-3FC6-4952-91AE-5E42D6558827 null
5 wax 0608CFF7-3FC6-4952-91AE-5E42D6558827 null
6 ear null null
7 yes D7CCBC4E-EEE6-4AC8-806D-A04DCC77DF54 null
8 sqt 5823E1FD-2AF3-4BA7-8C48-946A16E0D3E2 null
9 ora 5823E1FD-2AF3-4BA7-8C48-946A16E0D3E2 null
If I understand correctly, you can calculate the new ids for each phone number using a subquery and then use this for the update:
update u
set uid = pp.new_uid
from tbusertable u join
tbphtable p
on u.userid = p.userid join
(select phnos, newid() as new_uid
from tbphtable
group by phnos
) pp
on p.phnos = pp.phnos;
EDIT:
Because of how SQL Server optimizes queries, you might need to put the newids in a temporary table:
select phnos, newid() as new_uid
into #pp
from tbphtable
group by phnos;
And then:
update u
set uid = pp.new_uid
from tbusertable u join
tbphtable p
on u.userid = p.userid join
#pp pp
on p.phnos = pp.phnos;
SQL Server can rewrite queries, resulting in functions being called more commonly than expected. Putting the values in a temporary table should solve that problem.

sql show distinct column

I have a table like this and need to add column which will show info only in distinct rows:
ID Name
1 1001
2 1001
3 1001
4 1001
5 1002
6 1002
7 1002
8 1003
9 1004
10 1004
I need such result:
ID Name Result
1 1001 1
2 1001 NULL
3 1001 NULL
4 1001 NULL
5 1002 1
6 1002 NULL
7 1002 NULL
8 1003 1
9 1004 1
10 1004 NULL
The result column basically selects 1 only for distinct Name values.
This should be some basic query, but I cannot figure it.
Since ordering will wreak havoc with your concept, I'll assume that you'll always sort by ID.
You can do something like this:
select ID, Name,
case (select count(*) from table t2 where t2.Name=t1.Name and t1.ID > t2.ID)
when 0 then 1
else null
end as Result
from table t1
This will count all the rows with the same name, and with a lower ID than the row processed. If there are no such rows, then it's a new name, so it'll get the 1, otherwise, it will get a null value.
You can use row_number to get the first instance of a group, in this case Name:
select ID, Name,
case row_number() over(partition by Name order by ID)
when 1 then 1 end Result
from YourTable
Try that
SELECT ID,
NAME,
Case When x.minId Is Not Null Then 1 Else Null End As Result
FROM Table
left JOIN (
SELECT Name, min(id) minId
FROM Table ) x
on x.minId = Table.Id
and x.Name = Table.Name

lookup tables with temporary fields?

I have a query that produces the following results:
table1:
degree_code occupation_code degree_completions degree_level
1.0000 20-2021 10 1
1.0000 20-2022 10 1
1.1051 52-2095 2 3
1.1051 52-2095 41 2
1.5010 15-1100 2 3
I have another small, lookup table that relates degree_level to custom categories that I need to use:
table2
degree_level degree_level_recode
1 AADEGREE
2 AADEGREE
3 BACHDEGREE
I would like to build the output of of the first query to report the following format:
degree_code occupation_code degree_completions degree_level AADEGREE BACHDEGREE
1.0000 20-2021 10 1 10 0
1.0000 20-2022 10 1 10 0
1.1051 52-2095 2 3 3 0
1.1051 52-2095 41 2 0 41
1.5010 15-1100 2 3 2 1
Basically, create new temporary recode fields in the original query that report the degree_completions underneath them when they match the degree_level_recode, input 0 if no match. This is highly simplified; I will be performing operations on the recode fields against other fields in each element in the query.
I've shown the degree_completions field for reference, but I'd leave it out in the final query for obvious redundancy reasons.
I'm thinking I need to use a CASE statement or something similar for comparison checking, but I'm new to SQL.
EDIT:
In reference to Cha's answer below, take a revised table1 output (after mapping the recode):
degree_code degree_level degree_completions degree_level_recode
01.0601 2 11 LESSCOLL
01.0601 3 22 AADEGR
01.0605 2 3 LESSCOLL
Consider this code (table2 is referenced above the edit):
SELECT degree_code
,degree_level
,[LESSCOL] AS LESSCOL
,[AADEGR] AS AADEGR
,[BACHDEGR] AS BACHDEGR
,[MADEGR] AS MADEGR
,[DOCDEGR] AS DOCDEGR
FROM
(
SELECT degree_code
,table1.degree_level
,degree_level_recode
,degree_code
FROM table1
,table2
WHERE table1.degree_level = table2.degree_code
) AS p
PIVOT
(
SUM (degree_completions)
FOR degree_level_recode IN ([LESSCOL], [AADEGR], . . .)
) AS pvt
Which produces these results:
degree_code degree_level LESSCOL AADEGR BACHDEGR MADEGR DOCDEG
01.0601 2 NULL NULL NULL NULL NULL
01.0601 3 NULL 22 NULL NULL NULL
01.0505 2 NULL NULL NULL NULL NULL
I'm trying to get to:
degree_code degree_level LESSCOL AADEGR BACHDEGR MADEGR DOCDEG
01.0601 2 11 NULL NULL NULL NULL
01.0601 3 NULL 22 NULL NULL NULL
01.0505 2 3 NULL NULL NULL NULL
Additionally, replace NULL with 0.
This is what you are after (assumptions: your first table is called #temp1, your second table is called #temp2):
SELECT *
FROM
#temp1,
(SELECT degree_level, [AADEGREE] as col1, [BACHDEGREE] as col2
FROM
(SELECT degree_completions, #temp1.degree_level, degree_level_recode
FROM #temp1, #temp2 WHERE #temp1.degree_level = #temp2.degree_level) AS p
PIVOT
(
SUM (degree_completions)
FOR degree_level_recode IN
([AADEGREE], [BACHDEGREE])
) AS pvt
) as V
WHERE #temp1.degree_level = V.degree_level
ORDER BY 1