Add Min Value on Query Output in Separate Column - sql

I have the following table:
No Item Value
----------------------------
1 A 5
2 B 8
3 C 9
If I use Min function on Value field, then I'll get 5.
My question is, how can I put the MIN value into a new column? Like the following result:
No Item Value newCol
----------------------------
1 A 5 5
2 B 8 5
3 C 9 5
Is it possible to do that?
Thank you.

Something like:
select No, Item, Value, (select min(value) from table)
from table
should do it.

I'd prefer to do the subquery in a join, you'll have to name the field. Something like this;
Sample Data
CREATE TABLE #TestData (No int, item nvarchar(1), value int)
INSERT INTO #TestData (No, item, value)
VALUES
(1,'A',5)
,(2,'B',8)
,(3,'C',9)
Query
SELECT
td.No
,td.item
,td.value
,a.Min_Value
FROM #TestData td
CROSS JOIN
(
SELECT
MIN(Value) Min_Value
FROM #TestData
) a
Result
No item value Min_Value
1 A 5 5
2 B 8 5
3 C 9 5

You could do that even simpler by using an appropriate OVER() clause.
SELECT *
, MIN(Value) OVER () AS [newCol]
FROM Table
This would be simpler and less resource consuming than a (SELECT MIN(Value) FROM TABLE) in the top level SELECT.
Sample code:
DECLARE #tbl TABLE (No int, Item char(1), Value int)
INSERT #tbl VALUES (1, 'A', 5), (2, 'B', 8), (3, 'C', 9)
SELECT *
, MIN(Value) OVER () AS [newCol]
FROM #tbl

Using cross join with min value from table :
SELECT * FROM #Tbl1 CROSS JOIN (SELECT MIN(Value1) Value1 FROM #Tbl1) A

Related

How to set to update a column in a table that have more than one same value

I am trying to update a temporary table in a stored procedure, so in the set clause of the update, I used an inner join to update one column, but since that column has some same value the update gets repeated. How I can get the update only for the first unique value of the column?
Here is what I've done for the update:
select
style, color,item, prodnocompany, WIP, 0 As Inventory
into
#protable
from
prodorderheader poh
inner join
prodorderdetal pod on poh.prodno = pod.rpodno
select
item, sum(QOHQTY) as Inventory
into
#Inve
from
inventory
group by
item
update p
set p.Inventory += i.Inventory
from #prod p
inner join #Inve i on p.item = i.item
I need to get the following result:
Style
item
Inventory
D3623M
123776
12
D3623M
123665
11
T3445S
122099
10
D3565W
133422
12
D3565W
133422
0
In the #Prod table there may be some repeating of style and item, because they have a different prodnocompany, but in Inventory, each item has a specific quantity on the had amount (which is QOHQTY), but what I got from the update is as below:
Style
item
prodnocompany
Inventory
D3623M
123776
234
15
D3623M
123665
211
11
T3445S
122099
122
10
D3565W
133422
456
12
D3565W
133422
432
12
How can I update for the first item if I have the same item for different pronocompanys?
As an option, add the rank to the #prod table and then take only the first (last row) for updating.
For example, so:
declare
#t table (col varchar (100), i int)
declare
#t2 table (rn int, col varchar (100), i int)
insert into #t
select 'A', 100
union all
select 'B', 100
union all
select 'C', 200
union all
select 'C', 300
insert into #t2
select DENSE_RANK()over (partition by col order by i), * from #t
select * from #t2 t
where t.rn in (select min(t_s.rn) from #t2 t_s where t_s.col=t.col group by col)

Count length of consecutive duplicate values for each id

I have a table as shown in the screenshot (first two columns) and I need to create a column like the last one. I'm trying to calculate the length of each sequence of consecutive values for each id.
For this, the last column is required. I played around with
row_number() over (partition by id, value)
but did not have much success, since the circled number was (quite predictably) computed as 2 instead of 1.
Please help!
First of all, we need to have a way to defined how the rows are ordered. For example, in your sample data there is not way to be sure that 'first' row (1, 1) will be always displayed before the 'second' row (1,0).
That's why in my sample data I have added an identity column. In your real case, the details can be order by row ID, date column or something else, but you need to ensure the rows can be sorted via unique criteria.
So, the task is pretty simple:
calculate trigger switch - when value is changed
calculate groups
calculate rows
That's it. I have used common table expression and leave all columns in order to be easy for you to understand the logic. You are free to break this in separate statements and remove some of the columns.
DECLARE #DataSource TABLE
(
[RowID] INT IDENTITY(1, 1)
,[ID]INT
,[value] INT
);
INSERT INTO #DataSource ([ID], [value])
VALUES (1, 1)
,(1, 0)
,(1, 0)
,(1, 1)
,(1, 1)
,(1, 1)
--
,(2, 0)
,(2, 1)
,(2, 0)
,(2, 0);
WITH DataSourceWithSwitch AS
(
SELECT *
,IIF(LAG([value]) OVER (PARTITION BY [ID] ORDER BY [RowID]) = [value], 0, 1) AS [Switch]
FROM #DataSource
), DataSourceWithGroup AS
(
SELECT *
,SUM([Switch]) OVER (PARTITION BY [ID] ORDER BY [RowID]) AS [Group]
FROM DataSourceWithSwitch
)
SELECT *
,ROW_NUMBER() OVER (PARTITION BY [ID], [Group] ORDER BY [RowID]) AS [GroupRowID]
FROM DataSourceWithGroup
ORDER BY [RowID];
You want results that are dependent on actual data ordering in the data source. In SQL you operate on relations, sometimes on ordered set of relations rows. Your desired end result is not well-defined in terms of SQL, unless you introduce an additional column in your source table, over which your data is ordered (e.g. auto-increment or some timestamp column).
Note: this answers the original question and doesn't take into account additional timestamp column mentioned in the comment. I'm not updating my answer since there is already an accepted answer.
One way to solve it could be through a recursive CTE:
create table #tmp (i int identity,id int, value int, rn int);
insert into #tmp (id,value) VALUES
(1,1),(1,0),(1,0),(1,1),(1,1),(1,1),
(2,0),(2,1),(2,0),(2,0);
WITH numbered AS (
SELECT i,id,value, 1 seq FROM #tmp WHERE i=1 UNION ALL
SELECT a.i,a.id,a.value, CASE WHEN a.id=b.id AND a.value=b.value THEN b.seq+1 ELSE 1 END
FROM #tmp a INNER JOIN numbered b ON a.i=b.i+1
)
SELECT * FROM numbered -- OPTION (MAXRECURSION 1000)
This will return the following:
i id value seq
1 1 1 1
2 1 0 1
3 1 0 2
4 1 1 1
5 1 1 2
6 1 1 3
7 2 0 1
8 2 1 1
9 2 0 1
10 2 0 2
See my little demo here: https://rextester.com/ZZEIU93657
A prerequisite for the CTE to work is a sequenced table (e. g. a table with an identitycolumn in it) as a source. In my example I introduced the column i for this. As a starting point I need to find the first entry of the source table. In my case this was the entry with i=1.
For a longer source table you might run into a recursion-limit error as the default for MAXRECURSION is 100. In this case you should uncomment the OPTION setting behind my SELECT clause above. You can either set it to a higher value (like shown) or switch it off completely by setting it to 0.
IMHO, this is easier to do with cursor and loop.
may be there is a way to do the job with selfjoin
declare #t table (id int, val int)
insert into #t (id, val)
select 1 as id, 1 as val
union all select 1, 0
union all select 1, 0
union all select 1, 1
union all select 1, 1
union all select 1, 1
;with cte1 (id , val , num ) as
(
select id, val, row_number() over (ORDER BY (SELECT 1)) as num from #t
)
, cte2 (id, val, num, N) as
(
select id, val, num, 1 from cte1 where num = 1
union all
select t1.id, t1.val, t1.num,
case when t1.id=t2.id and t1.val=t2.val then t2.N + 1 else 1 end
from cte1 t1 inner join cte2 t2 on t1.num = t2.num + 1 where t1.num > 1
)
select * from cte2

SQL Server : build valid tree filtering invalid branches

I have a table with following data:
ID ParentID Name
-----------------------
1 NULL OK1
2 1 OK2
3 2 OK3
5 4 BAD1
6 5 BAD2
So I need to take only those lines, which are linked to ParentID = NULL OR valid children of such lines (i.e: OK3 is valid because it's linked to OK2, which is linked to OK1, which is linked to NULL, which is valid.)
But BAD1 and BAD 2 are not valid because those are not linked to a line, which is linked to NULL.
The best solution I figured out is a procedure + function. And function is called as many times as the max number of link levels in the table.
Can anybody suggest better solution for such task?
All you need is love, and a basic recursive CTE :-)
Create and populate sample data (Please save us this step in future questions):
DECLARE #T as table
(
ID int,
ParentID int,
Name varchar(4)
)
INSERT INTO #T VALUES
(1, NULL, 'OK1'),
(2, 1, 'OK2'),
(3, 2, 'OK3'),
(5, 4, 'BAD1'),
(6, 5, 'BAD2')
The CTE and query:
;WITH CTE AS
(
SELECT ID, ParentId, Name
FROM #T
WHERE ParentId IS NULL
UNION ALL
SELECT T1.ID, T1.ParentId, T1.Name
FROM #T T1
INNER JOIN CTE T2 ON T1.ParentID = T2.ID
)
SELECT *
FROM CTE
Results:
ID ParentId Name
----------- ----------- ----
1 NULL OK1
2 1 OK2
3 2 OK3

Sum the number of occurrence by id

Is it possible to COUNT the number of times a value occurs in a table, however, use the count of 1 if the value appears more than once for each id.
Take the below table as an example. We want to see if either {5,6} occurred for p_id. If more than 1 occurrence of {5,6} is found, treat it as 1. For eg. p_id 1, the total count is 1.
p_id status
1 5
1 6
1 2
2 5
2 5
3 4
3 2
4 6
4 2
4 5
..transforms to..
p_id count
1 1
2 1
3 0
4 1
COUNT(CASE status IN (5,6) THEN 1 END) does an overall count.
Use the CASE...WHEN... as follows:
SELECT a.id, ISNULL(b.cnt, 0)
FROM
(
SELECT DISTINCT id FROM tab
) a
LEFT JOIN
(
SELECT id, CASE COUNT(*) WHEN 1 THEN 0 ELSE 1 END 'cnt'
FROM tab WHERE val in (5, 6) GROUP BY id
) b
ON a.id = b.id
SQLFiddle
This solution provides a quick setup and a simple two-step explanation of how I do this, using your example. The second query provides the desired result:
CREATE TABLE #temp (p_id INT, [status] INT);
INSERT #temp VALUES (1,5);
INSERT #temp VALUES (1,6);
INSERT #temp VALUES (1,2);
INSERT #temp VALUES (2,5);
INSERT #temp VALUES (2,5);
INSERT #temp VALUES (3,4);
INSERT #temp VALUES (3,2);
INSERT #temp VALUES (4,6);
INSERT #temp VALUES (4,2);
INSERT #temp VALUES (4,5);
-- Simple two-step tutorial
-- First, group by p_id so that all p_id's will be shown
-- run this to see...
SELECT A.p_id
FROM #temp A
GROUP BY A.p_id;
-- Now expand your query
-- Next, for each p_id row found, perform sub-query to see if 1 or more exist with status=5 or 6
SELECT A.p_id
,CASE WHEN EXISTS(SELECT 1 FROM #temp B WHERE B.p_id=A.p_id AND [status] IN (5,6)) THEN 1 ELSE 0 END AS [Count]
FROM #temp A
GROUP BY A.p_id;
Use the SIGN() function. It is exactly what you are looking for.
SELECT
[p_id],
SIGN(COUNT(CASE WHEN [status] IN (5,6) THEN 1 END)) AS [count]
FROM #temp
GROUP BY p_id
You can translate 5,6 = 1 and rest to 0 then do max()
with cte as (
select p_id, case when status in (5,6) then 1 else 0 end status
from FROM #tem)
select p_id, max(status) status
from cte
group by p_id

Get max column with group by

I have a table for contents on a page. The page is divided into sections.
I want to get the last version for each page-section.
Id (int)
Version (int)
SectionID
Id Version SectionID Content
1 1 1 AAA
2 2 1 BBB
3 1 2 CCC
4 2 2 DDD
5 3 2 EEE
I want to get:
Id Version SectionID Content
2 2 1 BBB
5 3 2 EEE
You could use an exclusive self join:
select last.*
from YourTable last
left join
YourTable new
on new.SectionID = last.SectionID
and new.Version > last.Version
where new.Id is null
The where statement basically says: where there is no newer version of this row.
Slightly more readable, but often slower, is a not exists condition:
select *
from YourTable yt
where not exists
(
select *
from YourTable yt2
where yt2.SectionID = yt.SectionID
and yt2.Version > yt.Version
)
Example table definition:
declare #t table(Id int, [Version] int, [SectionID] int, Content varchar(50))
insert into #t values (1,1,1,'AAA');
insert into #t values (2,2,1,'BBB');
insert into #t values (3,1,2,'CCC');
insert into #t values (4,2,2,'DDD');
insert into #t values (5,3,2,'EEE');
Working solution:
select A.Id, A.[Version], A.SectionID, A.Content
from #t as A
join (
select max(C.[Version]) [Version], C.SectionID
from #t C
group by C.SectionID
) as B on A.[Version] = B.[Version] and A.SectionID = B.SectionID
order by A.SectionID
A simpler and more readeable solution:
select A.Id, A.[Version], A.SectionID, A.Content
from #t as A
where A.[Version] = (
select max(B.[Version])
from #t B
where A.SectionID = B.SectionID
)
I just saw that there was a very similar question for Oracle with an accepted answer based on performance.
Maybe if your table is big, an performance is an issue you can give it a try to see if SQL server also performs better with this:
select Id, Version, SectionID, Content
from (
select Id, Version, SectionID, Content,
max(Version) over (partition by SectionID) max_Version
from #t
) A
where Version = max_Version