SQL with WITH clause too many values error - sql

--This query is returning a "too many values" error. I'm not sure why?
update myTable
set var1= var2
WHERE value1 IN (
WITH X AS
(
select value1, value2, var1,var2, ROW_NUMBER()
OVER
(PARTITION BY value1 ORDER BY value1 desc) as rn
from
mytable WHERE var1 is null AND rownum>0 and rownum<=10 order by value1 asc
)
SELECT
value1, value2, var2,var1
FROM X WHERE rn=1 and var1 is null and rownum>0 and rownum<=10);

The error too many values is because the query has value1 IN () where multiple columns are being selected in the inner query. To avoid this, just select value1 in the inner query.
update myTable
set var1 = var2
WHERE value1 IN
(select value1 from
(select value1, value2, var1,var2,
ROW_NUMBER() OVER (PARTITION BY value1 ORDER BY value1 desc) as rn
from mytable
WHERE var1 is null
) t
where rn <= 10
)

I think your intention is better served using exists:
update myTable
set var1 = var2
where exists (select 1
from (select value1, value2, var1, var2,
ROW_NUMBER() OVER (PARTITION BY value1 ORDER BY value1 desc) as rn
from mytable
where var1 is null
) x
where mytable.value1 in (x.value1, x.value2, x.var1, x.var2) and
rn <= 10
);

Related

Sum the two lowest values from a record

I have a table like this:
I need to sum the two lowest values for each record. For example, in the first row 2 and 4 (2 + 4 = 6).
I can find the lowest value for each row using CROSS APPLY, but I can't find the two lowest values at once to sum them.
Thanks in advance.
I would do this as:
select id, sumval - maxval
from t cross apply
(select sum(val) as sumval, max(val) as maxval
from values (value1), (value2), (value3)) v(val)
) v;
If you have three items, the sum of the smallest two is the sum of all of them minus the largest.
More generally, I would use something like this:
select id, sum2
from t cross apply
(select sum(val) as sum2
from (select top (2) val
from values (value1), (value2), (value3) v(val)
order by val asc
) v
) v
SELECT
IIF (VALUE1 < VALUE3 AND VALUE2 < VALUE3,
VALUE1 + VALUE2,
IIF(VALUE1 < VALUE2 AND VALUE3 < VALUE2,
VALUE1 + VALUE3,
IIF(VALUE3 < VALUE1 AND VALUE2 < VALUE1,
VALUE2 + VALUE3, 0)))
-- You will have to decide what to do if none of the conditions are met: I set the result to zero. This gets unwieldy if you add more columns
Initial data:
DECLARE #Table TABLE (ID INT IDENTITY(1,1),Value1 INT, Value2 INT, Value3 INT);
INSERT INTO #Table (Value1,Value2,Value3) VALUES
(2,4,5)
,(3,7,2)
,(9,1,6)
;
The code:
SELECT a.ID,SUM(a.[Value]) AS [Sum]
FROM (
SELECT p.ID,p.Value
,ROW_NUMBER()OVER(PARTITION BY p.ID ORDER BY p.Value ASC) AS [rn]
FROM #Table t
UNPIVOT(Value FOR Param IN ([Value1],[Value2],[Value3])) p
) a
WHERE a.rn <= 2 /*pick up only two lowest*/
GROUP BY a.ID
;

SQL - sort of SUM with varchar

have a (weird) table looking like this
ID Version Value1 Value2 Value3
1 1 Shaft
1 2 steel xy
2 1 Knife somethins
2 3 Super
Want to merge, need to have this result, by using Value from the highest Version, that has content:
ID Value1 Value2 Value3
1 Shaft steel xy
2 Super Knife somethin
as far as I know Group using Max(Version) would bring the NULL values of highest Version row.
something like SUM?
Second try... There are probably shorter and nicer solutions, but it should work:
with
v1 as
(
select w1.id, w1.value1 from weird w1
where w1.value1 is not null
and w1.version=(select max(w11.version) from weird w11 where w11.id=w1.id and w11.value1 is not null)
),
v2 as
(
select w2.id, w2.value2 from weird w2
where w2.value2 is not null
and w2.version=(select max(w22.version) from weird w22 where w22.id=w2.id and w22.value2 is not null)
),
v3 as
(
select w3.id, w3.value3 from weird w3
where w3.value3 is not null
and w3.version=(select max(w33.version) from weird w33 where w33.id=w3.id and w33.value3 is not null)
)
select v1.id, v1.value1, v2.value2, v3.value3
from v1, v2, v3
where v1.id=v2.id and v1.id=v3.id;
We can use UNPIVOT and PIVOT creatively to construct the data you want:
declare #t table (ID int not null, Version int not null, Value1 varchar(20) null,
Value2 varchar(20) null, Value3 varchar(20) null)
insert into #t(ID,Version,Value1,Value2,Value3) values
(1,1,'Shaft',null,null),
(1,2,null,'steel','xy'),
(2,1,null,'Knife','somethins'),
(2,3,'Super',null,null)
;With Numberable as (
select *,ROW_NUMBER() OVER (PARTITION BY ID,Val ORDER BY Version desc) rn
from #t t
unpivot (tdata for Val in (Value1,Value2,Value3)) u
), Selected as (
select ID,tdata,Val
from Numberable where rn = 1
)
select
*
from Selected s
pivot (MAX(tdata) for Val in (Value1,Value2,Value3)) u
The UNPIVOT automatically removes the NULLs. The ROW_NUMBER() identifies the values we want to keep. The Selected CTE hides the columns we no longer need so that the PIVOT creates the final result we want:
ID Value1 Value2 Value3
----------- -------------------- -------------------- --------------------
1 Shaft steel xy
2 Super Knife somethins
(I'm using MAX in the pivot but that's just to satisfy the optimizer. Because we've only selected one row for each ID, Val combination, we know that at most one value will be selected to appear in a final position in the grid formed by the pivot)
The above does make the assumption that Value1,Value2 and Value3 all have the same, or at least compatible, data types.
You can rank the values with row_number. The following query first builds such ranks. rn1 is built per id and value1 is null/not null in the descending order of the version. So per ID we get #1 for the last null value and the last filled value. Later we use rn1 = 1 to get the maximum of the two, which is the last filled value. Same for rn2/value2 and rn3/value3.
select
id,
min(case when rn1 = 1 then value1 end) as value1,
min(case when rn2 = 1 then value2 end) as value2,
min(case when rn3 = 1 then value3 end) as value3
from
(
select
id, value1, value2, value3,
row_number() over (partition by id, case when value1 is null then 0 else 1 end order by version desc) as rn1,
row_number() over (partition by id, case when value2 is null then 0 else 1 end order by version desc) as rn2,
row_number() over (partition by id, case when value3 is null then 0 else 1 end order by version desc) as rn3
from mytable
) ranked
group by id
order by id;
Used CASE WHEN to SELECT max(version) where value is not null and not blank and then joinedwith the original table on those versions. You can see it in action in link provided below the query
Use this query.
Select distinct a.*, b.value1, c.value2, d.value3
from
(
Select id, max(case when (value1 is not null and value1 <> ' ') then version else 0 end) as ver1,
max(case when (value2 is not null and value2 <> ' ') then version else 0 end) as ver2,
max(case when (value3 is not null and value3 <> ' ') then version else 0 end) as ver3
from
your_table
group by id
) a
inner join
your_table b,
your_table c,
your_table d
where (a.ver1=b.version and a.id=b.id)
and (a.ver2=c.version and a.id=c.id)
and (a.ver3=d.version and a.id=d.id)
See it in action here at this link

find row number by group in SQL server table with duplicated rows

I need to count the row number by group in a table with some duplications.
Table:
id va1ue1 value2
1 3974 39
1 3974 39
1 972 5
1 972 10
SQL:
select id, value1, value2, COUNT(*) cnt
FROM table
group by id, value1, value2
having COUNT(*) > 1
The code only count the duplicated rows.
I need:
id, value1, value2
1 972 5
1 972 10
I do not need to count the duplicated rows, I only need the rows that value1 has more than one distinct values in value2 column.
Thanks
Use DISTINCT:
select id, value1, count(distinct value2) cnt
from table
group by id, value1
having count(distinct value2) > 1
If you want detais then:
select * from table t1
cross apply(select cnt from(
select count(distinct value2) cnt
from table t2
where t1.id = t2.id and t1.value1 = t2.value1) t
where cnt > 1)ca
In SQL Server 2008, you can use a trick to count distinct values using window functions. You might find this a nice solution:
select t.id, t.value1, t.value2
from (select t.*, sum(case when seqnum = 1 then 1 else 0 end) over (partition by value1) as numvals
from (select t.*, row_number() over (partition by value1, value2 order by (select null)) as seqnum
from table t
) t
) t
where numvals > 1;
Try it this way without a GROUP BY:
select id, value1, value2
FROM table AS T1
where 1 < (
select COUNT(*)
FROM table AS T2
where T1.value1 = T2.value1)
Try this
;WITH CTE
AS ( SELECT id ,
value1 ,
value2 ,
COUNT(*) cnt
FROM table
GROUP BY id ,
value1 ,
value2
HAVING COUNT(*) > 1
)
SELECT *
FROM table1
WHERE value1 IN ( SELECT value1
FROM CTE )
Simply use a NOT after HAVING, which precisely gets you the rows which are NOT duplicated.
select id, value1, value2
FROM [table]
group by id, value1, value2
having NOT COUNT(*) > 1
Fiddle here.
If you want the actual rows from the table, not just the qualifying id, value1 pairs, you could do this:
WITH discrepancies AS (
SELECT,
id,
value1,
value2,
distinctcount = COUNT(DISTINCT value2) OVER (PARTITION BY id, value1)
FROM
dbo.atable
)
SELECT
id,
value1,
value2
FROM
discrepancies
WHERE
distinctcount > 1
;
if SQL Server 2008 supported COUNT(DISTINCT ...) with an OVER clause.
Basically, it would be the same idea as Giorgi Nakeuri's one, more or less, except you would not be hitting the table more than once.
Alas, there is no support for COUNT(DISTINCT ...) OVER ... in SQL Server so far. Still, you can use a different method, which will still allow you to touch the table just once and return detail rows nevertheless:
WITH discrepancies AS (
SELECT,
id,
value1,
value2,
minvalue2 = MIN(value2) OVER (PARTITION BY id, value1),
maxvalue2 = MAX(value2) OVER (PARTITION BY id, value1)
FROM
dbo.atable
)
SELECT
id,
value1,
value2
FROM
discrepancies
WHERE
minvalue2 <> maxvalue2
;
The idea here is to get MIN(value2) and MAX(value2) per each id, value1 and to see if those differ. If they do, that means you have a discrepancy in this id, value1 subset and you want that row to be returned.
The method takes advantage of aggregates with an OVER clause to avoid a self-join, and that is precisely the reason why the table is accessed just once here.

How to get a difference between two rows

I want to find the difference between two rows on the same column group by id
ID Value1 Value2
a 500 200
b 300 200
a 100 300
b 300 400
....
Expected output
ID Value1 Value2
a 400 -100
b 0 -200
....
How to make a query for the above condition.
You can use following:
SELECT
ID,
MAX(Value1) - MIN(Value1),
MIN(Value2) - MAX(Value2)
FROM
myTableName
GROUP BY
ID
But there is one assumption: the second row has always greater Value1 and lower Value2 than first one.
You can try:
SELECT t1.ID, max(t2.VALUE1 - t1.VALUE1)
FROM TABLE1 t1
left join TABLE1 t2 on t1.id = t2.id
group by t1.id
SQL FIDDLE DEMO:
This query will give a absolute difference between max value of ID and min value of same ID:
SELECT ID
, ABS(MAX(VALUE1) - MIN(VALUE1)) AS v1Diff
, ABS(MAX(VALUE2) - MIN(VALUE2)) AS v2Diff
FROM TABLE1
GROUP BY ID
Sql Fiidle
But if you want get a real difference(negative diff) then we need to know which row is first and which row is next. Then we can count difference like firstRowValue - nextRowValue.
Maybe your table has some RowID or DateTime column from where we can ordering a rows from same ID.
What column/columns are Primery Key in your table?
Use option with CTE and ROW_NUMBER() ranking function
;WITH cte AS
(
SELECT ID,
CASE ROW_NUMBER() OVER(PARTITION BY ID ORDER BY 1/0) % 2
WHEN 1 THEN Value1
WHEN 0 THEN -1 * Value1 END AS Value1,
CASE ROW_NUMBER() OVER(PARTITION BY ID ORDER BY 1/0) % 2
WHEN 1 THEN Value2
WHEN 0 THEN -1 * Value2 END AS Value2
FROM dbo.test22
)
SELECT ID, SUM(Value1) AS Value1, SUM(Value2) AS Value2
FROM cte
GROUP BY ID
Demo on SQLFiddle

SQL Server / T-SQL : How to update equal percentages of a resultset?

I need a way to take a resultset of KeyIDs and divide it up as equally as possible and update records differently for each division based on the KeyIDs. In other words, there is
SELECT KeyID
FROM TableA
WHERE (some criteria exists)
I want to update TableA 3 different ways by 3 equal portions of KeyIDs.
UPDATE TableA
SET FieldA = Value1
WHERE KeyID IN (the first 1/3 of the SELECT resultset above)
UPDATE TableA
SET FieldA = Value2
WHERE KeyID IN (the second 1/3 of the SELECT resultset above)
UPDATE TableA
SET FieldA = Value3
WHERE KeyID IN (the third 1/3 of the SELECT resultset above)
or something to that effect. Thanks for any and all of your responses.
With TiledItems As
(
Select KeyId
, NTILE(3) OVER( ORDER BY ... ) As NTileNum
From TableA
Where ...
)
Update TableA
Set FieldA = Case TI.NTileNum
When 1 Then Value1
When 2 Then Value2
When 3 Then Value3
End
From TableA As A
Join TiledItems As TI
On TI.KeyId = A.KeyId
Unfortunately I haven't got time to knock up a complete solution but the gist of one would be to use a CTE with the NTILE function http://msdn.microsoft.com/en-us/library/ms175126.aspx to divide into 3 groups then join onto that CTE in your UPDATE statement and do a CASE statement against the NTILE group to determine whether to use Value1, Value2, or Value3.
Edit
See Thomas's answer for the code for this as looks like he had the same idea!
For a simple distribution, create a random ranking and modulo by 3...
UPDATE
A
SET
FieldA =
CASE Ranking % 3
WHEN 1 THEN B.Value1
WHEN 2 THEN B.Value2
WHEN 0 THEN B.Value3
END
FROM
TableA A
inner join
(SELECT
ID,
ROW_NUMBER() OVER (ORDER BY ID /*or something*/) AS Ranking,
Value1, Value2, Value3
FROM
TableA
) B on A.ID = B.ID
where (some criteria exists)
You can change the ORDER BY for the ROW_NUMBER(), or use NTILE and remove the modulo
If the keys are evenly-distributed, then you could use the modulus (%) operator to select out unique thirds of the result set.
update TableA set FieldA = Value1 where KeyID % 3 = 0;
update TableA set FieldA = Value2 where KeyID % 3 = 1;
update TableA set FieldA = Value3 where KeyID % 3 = 2;
Interpreting what you say literally, you could number the rows in the returned row set, and then select the different segements based on their row number.
E.g.
UPDATE TableA
SET FieldA = Value1
WHERE KeyID IN (SELECT * FROM (SELECT <your rows>, ROW_NUMBER() (ORDER BY <anyRow>) AS RowNumber FROM <yourTable> ) base
WHERE RowNumber<Count(RowNumber)/3)
UPDATE TableA
SET FieldA = Value1
WHERE KeyID IN (SELECT * FROM (SELECT <your rows>, ROW_NUMBER() (ORDER BY <anyRow>) AS RowNumber FROM <yourTable> ) base
WHERE RowNumber<Count(RowNumber)*2/3 && RowNumber>=Count(RowNumber)/3)
UPDATE TableA
SET FieldA = Value1
WHERE KeyID IN (SELECT * FROM (SELECT <your rows>, ROW_NUMBER() (ORDER BY <anyRow>) AS RowNumber FROM <yourTable> ) base
WHERE owNumber>=Count(RowNumber)*2/3)
WITH Query (OtherKeyID, PCT)
AS
(
SELECT KeyID, (ROW_NUMBER() OVER (ORDER BY KeyID)) / foo.CNT AS PCT
FROM TableA
JOIN (SELECT CONVERT(float, COUNT(1)) AS CNT FROM TableA) foo ON 1 = 1
WHERE (criteria)
)
UPDATE TableA
SET FieldA = (CASE
WHEN PCT < .3333 THEN Value1
WHEN PCT BETWEEN .3333 and .6666 THEN Value2
WHEN PCT > .6666 THEN Value3 ELSE NULL END)
FROM Query
WHERE KeyID = OtherKeyID AND PCT < .3333
Note that you can alter the ORDER BY clause in the query to any valid expression, which will allow you to define your "first third" by any criteria.