SELECT Columns WHERE one column Is Distinct? - tsql

Can anyone help me with the query ?
I've tried the following but it comes up with an error
SELECT Column1, Column2, Column3 FROM Table WHERE [Column1] NOT IN
(SELECT [Column1] FROM Table GROUP BY [Column1] HAVING COUNT([Column]) > 1)
Invalid MEMO, OLE, or Hyperlink Object in subquery [Column1].

Use Group By with Having clause:
SELECT Column1, MIN(Column2)AS Column2, MIN(Column3)AS Column3
FROM dbo.Table
GROUP BY Column1
HAVING ( COUNT(Column1) = 1 )
Should work since there's only one row per "group".

Your original query should work you just had column instead of column1.
SELECT Column1, Column2, Column3 FROM TableName WHERE [Column1] NOT IN
(SELECT [Column1] FROM TableName GROUP BY [Column1] HAVING COUNT(Column1) > 1)
see: http://sqlfiddle.com/#!3/d99a8/5/0

As far as i get , you need all data where [Column1] is unique (appear just one time)
DECLARE #x TABLE (col1 INT, col2 INT, col3 INT)
INSERT INTO #x
( [col1], [col2], [col3] )
VALUES ( 1, 2, 3 )
,( 1, 4, 5 )
,( 2, 6, 7 )
SELECT * FROM #x
SELECT col1, col2 , col3 FROM #x
WHERE col1 NOT IN
( SELECT [col1] FROM #x GROUP BY [col1] HAVING COUNT(*) > 1 )

Related

Concatenating NULL returns a value in T-SQL (CONCAT function)

Why does this return . when col1 contains a blank value?
CONCAT(NULLIF([COL1],''),'.')
I have 3 columns that i need to concatenate with a . in between, sometimes the column contains a blank value. In that case the trailing . should not be concatenated. What functions do I use?
col1 col2 col3
A 1 x
B 2
expected results:
A.1.X
B.2
test code:
DECLARE #tbl TABLE(a varchar(100),b varchar(100),c varchar(100))
INSERT INTO #tbl
SELECT 'A','1','X' UNION
SELECT 'B','2','' UNION
SELECT 'C','','' UNION
SELECT '','1','X' UNION
SELECT 'B','','' UNION
SELECT 'C','',''
SELECT CONCAT ( Nullif(a,''),'.' + nullif(b,''), '.' + nullif(c,'')) AS Contact_Result FROM #tbl;
You can use SQL CONCAT this way
SELECT CONCAT ( a,IIF((NULLIF(a,'')+NULLIF(b,'')) IS NULL,'','.'),b,IIF((NULLIF(b,'')+NULLIF(c,'')) IS NULL,'','.'), c) AS Contact_Result FROM #tbl;
Test code below
DECLARE #tbl TABLE(a varchar(100),b varchar(100),c varchar(100))
INSERT INTO #tbl
SELECT 'A','1','X' UNION
SELECT 'B','2',NULL UNION
SELECT 'C',NULL,NULL
SELECT CONCAT ( a,IIF((NULLIF(a,'')+NULLIF(b,'')) IS NULL,'','.'),b,IIF((NULLIF(b,'')+NULLIF(c,'')) IS NULL,'','.'), c) AS Contact_Result FROM #tbl;
Contact_Result
A.1.X
B.2
C
Another common use of this kind of concats is to Concat a Full Name in this case the . (dot) is replaced by a ' ' (space), it makes things easier because you can use trim
DECLARE #tbl TABLE(a varchar(100),b varchar(100),c varchar(100))
INSERT INTO #tbl
SELECT 'FirtName','MiddleName','LastName' UNION
SELECT 'FistName','','LastName' UNION
SELECT '','','FullName'
SELECT LTRIM(CONCAT ( a,' ' + b,' ' + c)) AS Contact_Result FROM #tbl;
Result
FullName
FirtName MiddleName LastName
FistName LastName
THIS IS AN ANSWER THAT COVERS ALL POSSIBILITIES
SELECT SUBSTRING(CONCAT ('.' + NULLIF(a,''),'.' + NULLIF(b,''),'.' + NULLIF(c,'')),2,10000) AS Contact_Result FROM #tbl;
Complete test cases
DECLARE #tbl TABLE(a varchar(100),b varchar(100),c varchar(100))
INSERT INTO #tbl
SELECT 'a','','' UNION
SELECT 'a','b','' UNION
SELECT 'a','b','c' UNION
SELECT 'a','','c' UNION
SELECT '','b','c' UNION
SELECT '','b','' UNION
SELECT '','','c' UNION
SELECT '','',''
SELECT SUBSTRING(CONCAT ('.' + NULLIF(a,''),'.' + NULLIF(b,''),'.' + NULLIF(c,'')),2,10000) AS Contact_Result FROM #tbl;
Results
c
b
b.c
a
a.c
a.b
a.b.c
You'll have to go old school. I'm on my phone and can't test this.
ISNULL(NULLIF([COL1],'') + '.', '') +
ISNULL(NULLIF([COL2],'') + '.', '') +
ISNULL(NULLIF([COL3],''), '');
---------------- EDIT ----------------
Okay, this was not as easy on my phone as I thought. Here's my updated solution that works with SQL Server 2005+
-- sample data
declare #table table
(
id int identity,
col1 varchar(10),
col2 varchar(10),
col3 varchar(10)
);
insert #table (col1, col2, col3)
values ('a','b','c'), ('aa','bb',''), ('x','','z'), ('','p','pp'),
('!!!','',''), ('','','fff'), ('','','');
-- My solution
select col1, col2, col3, concatinatedValue =
case when cv like '.%' then stuff(cv, 1, 1,'') else cv end
from #table
cross apply (values
(isnull(nullif([col1],''), '') +
isnull('.'+nullif([col2],''), '') +
isnull('.'+nullif([col3],''), ''))) v(cv);
RETURNS
cv col1 col2 col3 concatinatedValue
--------- ----- ----- ----- -------------------
a.b.c a b c a.b.c
aa.bb aa bb aa.bb
x.z x z x.z
.p.pp p pp p.pp
!!! !!! !!!
.fff fff fff
<----------- blank line ----------->

PostgreSQL grouping

I would like to group values according to values in over columns.
This is an example:
I would like to get the output:
{{-30,-50,20},{-20,30,60},{-30,NULL or other value, 20}}
I managed to arrive to:
SELECT array_agg("val")
FROM my_table
WHERE "t_id" = 1
GROUP BY "m_id";
{{-30,-50,20},{-20,30,60},{-30,20}}
What would be the best approach?
create table my_table (
t_id int,
m_id int,
s_id int,
val int
);
insert into my_table (t_id, m_id, s_id, val) values
(1,1,1,-30),
(1,1,2,-50),
(1,1,3,20),
(1,2,1,-20),
(1,2,2,30),
(1,2,3,60),
(1,3,1,-30),
(1,3,3,20);
select array_agg(val order by s_id)
from
my_table t
right join
(
(
select distinct t_id, m_id
from my_table
) a
cross join
(
select distinct s_id
from my_table
) b
) s using (t_id, m_id, s_id)
where t_id = 1
group by m_id
order by m_id
;
array_agg
---------------
{-30,-50,20}
{-20,30,60}
{-30,NULL,20}

SQL Server 2008 T-SQL UDF Split() Tailoring

I'm useing SQL Ser 2008 and have a large table with only one column of data. The data is a random string with very little consistency. Eample: Name Account 445566 0010020056893010445478008 AFD 369. I've been working with a split function that a stackoverflow user suggested. It works great but the function assigns the split string into one column. I need a row of individual columns. The present result is 1col with values Name, Account, 445566,... in it but the result I'm looking for is col1 Name, col2 Account, col3 445566,...
If anyone could provide some insight on how to tailor this script or its usage to get the desired result it would be much appreciated.
CREATE FUNCTION [dbo].[Split]
(
#String varchar(max)
,#Delimiter char
)
RETURNS #Results table
(
Ordinal int
,StringValue varchar(max)
)
as
begin
set #String = isnull(#String,'')
set #Delimiter = isnull(#Delimiter,'')
declare
#TempString varchar(max) = #String
,#Ordinal int = 0
,#CharIndex int = 0
set #CharIndex = charindex(#Delimiter, #TempString)
while #CharIndex != 0 begin
set #Ordinal += 1
insert #Results values
(
#Ordinal
,substring(#TempString, 0, #CharIndex)
)
set #TempString = substring(#TempString, #CharIndex + 1, len(#TempString) - #CharIndex)
set #CharIndex = charindex(#Delimiter, #TempString)
end
if #TempString != '' begin
set #Ordinal += 1
insert #Results values
(
#Ordinal
,#TempString
)
end
return
end
--The usage:
SELECT
*
FROM
mytable M
CROSS APPLY
[dbo].[Split] (M.TheColumn, ' ') S
Where rtrim(s.StringValue) != ''
If you know that you have 6 columns in the string you can use a split functions that looks like this and of course modify the function to whatever number of columns you want. A function can not return a dynamic number of columns.
create function dbo.Split6(#String varchar(max), #Delimiter char(1))
returns table as return
(
select
substring(T.Col, 1, S1.Pos-1) as Col1,
substring(T.Col, S1.Pos+1, S2.Pos-S1.Pos-1) as Col2,
substring(T.Col, S2.Pos+1, S3.Pos-S2.Pos-1) as Col3,
substring(T.Col, S3.Pos+1, S4.Pos-S3.Pos-1) as Col4,
substring(T.Col, S4.Pos+1, S5.Pos-S4.Pos-1) as Col5,
substring(T.Col, S5.Pos+1, S6.Pos-S5.Pos-1) as Col6
from (select #String+replicate(#Delimiter, 6)) as T(Col)
cross apply (select charindex(#Delimiter, T.Col, 1)) as S1(Pos)
cross apply (select charindex(#Delimiter, T.Col, S1.Pos+1)) as S2(Pos)
cross apply (select charindex(#Delimiter, T.Col, S2.Pos+1)) as S3(Pos)
cross apply (select charindex(#Delimiter, T.Col, S3.Pos+1)) as S4(Pos)
cross apply (select charindex(#Delimiter, T.Col, S4.Pos+1)) as S5(Pos)
cross apply (select charindex(#Delimiter, T.Col, S5.Pos+1)) as S6(Pos)
)
Test:
declare #T table (Col varchar(100))
insert into #T values
('Name Account 445566 0010020056893010445478008 AFD 369'),
(''),
('1 2'),
('1 3')
select S.Col1, S.Col2, S.Col3, S.Col4, S.Col5, S.Col6
from #T as T
cross apply
dbo.Split6(T.Col, ' ') as S
Result:
Col1 Col2 Col3 Col4 Col5 Col6
---- ------- ------ ------------------------- ---- ----
Name Account 445566 0010020056893010445478008 AFD 369
1 2
1 3
You might try using a PIVOT.
http://msdn.microsoft.com/en-us/library/ms177410.aspx

T-SQL -- convert comma-delimited column into multiple columns

From the table below, how can I convert the Values column into multiple columns, populated with individual values that are currently separated by commas? Before the conversion:
Name Values
---- ------
John val,val2,val3
Peter val5,val7,val9,val14
Lesli val8,val34,val36,val65,val71,val
Amy val3,val5,val99
The result of the conversion should look like:
Name Col1 Col2 Col3 Col4 Col5 Col6
---- ---- ---- ---- ---- ---- ----
John val val2 val3
Peter val5 val7 val9 val14
Lesli val8 val34 val36 val65 val71 val
Amy val3 val5 val99
First, what database product and version are you using? If you are using SQL Server 2005 and later, you can write a Split user-defined function like so:
CREATE FUNCTION [dbo].[Split]
(
#DelimitedList nvarchar(max)
, #Delimiter varchar(2) = ','
)
RETURNS TABLE
AS
RETURN
(
With CorrectedList As
(
Select Case When Left(#DelimitedList, DataLength(#Delimiter)) <> #Delimiter Then #Delimiter Else '' End
+ #DelimitedList
+ Case When Right(#DelimitedList, DataLength(#Delimiter)) <> #Delimiter Then #Delimiter Else '' End
As List
, DataLength(#Delimiter) As DelimiterLen
)
, Numbers As
(
Select TOP (Coalesce(Len(#DelimitedList),1)) Row_Number() Over ( Order By c1.object_id ) As Value
From sys.objects As c1
Cross Join sys.columns As c2
)
Select CharIndex(#Delimiter, CL.list, N.Value) + CL.DelimiterLen As Position
, Substring (
CL.List
, CharIndex(#Delimiter, CL.list, N.Value) + CL.DelimiterLen
, CharIndex(#Delimiter, CL.list, N.Value + 1)
- ( CharIndex(#Delimiter, CL.list, N.Value) + CL.DelimiterLen )
) As Value
From CorrectedList As CL
Cross Join Numbers As N
Where N.Value < Len(CL.List)
And Substring(CL.List, N.Value, CL.DelimiterLen) = #Delimiter
)
You can then split out the values in you want using something akin to:
Select Name, Values
From Table1 As T1
Where Exists (
Select 1
From Table2 As T2
Cross Apply dbo.Split (T1.Values, ',') As T1Values
Cross Apply dbo.Split (T2.Values, ',') As T2Values
Where T2.Values.Value = T1Values.Value
And T1.Name = T2.Name
)
Here is a solution that uses a recursive cte to generate a "table of numbers" (courtesy of Itzik Ben-Gan), which is useful for all manner of problems including string splitting, and PIVOT. SQL Server 2005 onwards. Full table create, insert and select script included.
CREATE TABLE dbo.Table1
(
Name VARCHAR(30),
[Values] VARCHAR(128)
)
GO
INSERT INTO dbo.Table1 VALUES ('John', 'val,val2,val3')
INSERT INTO dbo.Table1 VALUES ('Peter', 'val5,val7,val9,val14')
INSERT INTO dbo.Table1 VALUES ('Lesli', 'val8,val34,val36,val65,val71,val')
INSERT INTO dbo.Table1 VALUES ('Amy', 'val3,val5,val99')
GO
SELECT * FROM dbo.Table1;
GO
WITH
L0 AS(SELECT 1 AS c UNION ALL SELECT 1),
L1 AS(SELECT 1 AS c FROM L0 AS A, L0 AS B),
L2 AS(SELECT 1 AS c FROM L1 AS A, L1 AS B),
L3 AS(SELECT 1 AS c FROM L2 AS A, L2 AS B),
Numbers AS(SELECT ROW_NUMBER() OVER(ORDER BY c) AS n FROM L3)
SELECT Name, [1] AS Column1, [2] AS Column2, [3] AS Column3, [4] AS Column4, [5] AS Column5, [6] AS Column6, [7] AS Column7
FROM
(SELECT Name,
ROW_NUMBER() OVER (PARTITION BY Name ORDER BY nums.n) AS PositionInList,
LTRIM(RTRIM(SUBSTRING(valueTable.[Values], nums.n, charindex(N',', valueTable.[Values] + N',', nums.n) - nums.n))) AS [Value]
FROM Numbers AS nums INNER JOIN dbo.Table1 AS valueTable ON nums.n <= CONVERT(int, LEN(valueTable.[Values])) AND SUBSTRING(N',' + valueTable.[Values], n, 1) = N',') AS SourceTable
PIVOT
(
MAX([VALUE]) FOR PositionInList IN ([1], [2], [3], [4], [5], [6], [7])
) AS Table2
GO
--DROP TABLE dbo.Table1
Which converts this output
Name Values
John val,val2,val3
Peter val5,val7,val9,val14
Lesli val8,val34,val36,val65,val71,val
Amy val3,val5,val99
to
Name Column1 Column2 Column3 Column4 Column5 Column6 Column7
Amy val3 val5 val99 NULL NULL NULL NULL
John val val2 val3 NULL NULL NULL NULL
Lesli val8 val34 val36 val65 val71 val NULL
Peter val5 val7 val9 val14 NULL NULL NULL

Aggregate GREATEST in T-SQL

My SQL is rusty -- I have a simple requirement to calculate the sum of the greater of two column values:
CREATE TABLE [dbo].[Test]
(
column1 int NOT NULL,
column2 int NOT NULL
);
insert into Test (column1, column2) values (2,3)
insert into Test (column1, column2) values (6,3)
insert into Test (column1, column2) values (4,6)
insert into Test (column1, column2) values (9,1)
insert into Test (column1, column2) values (5,8)
In the absence of the GREATEST function in SQL Server, I can get the larger of the two columns with this:
select column1, column2, (select max(c)
from (select column1 as c
union all
select column2) as cs) Greatest
from test
And I was hoping that I could simply sum them thus:
select sum((select max(c)
from (select column1 as c
union all
select column2) as cs))
from test
But no dice:
Msg 130, Level 15, State 1, Line 7
Cannot perform an aggregate function on an expression containing an aggregate or a subquery.
Is this possible in T-SQL without resorting to a procedure/temp table?
UPDATE: Eran, thanks - I used this approach. My final expression is a little more complicated, however, and I'm wondering about performance in this case:
SUM(CASE WHEN ABS(column1 * column2) > ABS(column3 * column4)
THEN column5 * ABS(column1 * column2) * column6
ELSE column5 * ABS(column3 * column4) * column6 END)
Try this:
SELECT SUM(CASE WHEN column1 > column2
THEN column1
ELSE column2 END)
FROM test
Try this... Its not the best performing option, but should work.
SELECT
'LargerValue' = CASE
WHEN SUM(c1) >= SUM(c2) THEN SUM(c1)
ELSE SUM(c2)
END
FROM Test
SELECT
SUM(MaximumValue)
FROM (
SELECT
CASE WHEN column1 > column2
THEN
column1
ELSE
column2
END AS MaximumValue
FROM
Test
) A
FYI, the more complicated case should be fine, so long as all of those columns are part of the same table. It's still looking up the same number of rows, so performance should be very similar to the simpler case (as SQL Server performance is usually IO bound).
How to find max from single row data
-- eg (empid , data1,data2,data3 )
select emplid , max(tmp.a)
from
(select emplid,date1 from table
union
select emplid,date2 from table
union
select emplid,date3 from table
) tmp , table
where tmp.emplid = table.emplid
select sum(id) from (
select (select max(c)
from (select column1 as c
union all
select column2) as cs) id
from test
)
The best answer to this is simply put :
;With Greatest_CTE As
(
Select ( Select Max(ValueField) From ( Values (column1), (column2) ) ValueTable(ValueField) ) Greatest
From Test
)
Select Sum(Greatest)
From Greatest_CTE
It scales a lot better than the other answers with more than two value columns.