SQL Group by with concat - sql

Hi
Can anybody help me with the following. I need to write a MS SQL statment to achive the following:
Table1 has 2 columns: Column1 and Column2
Data in table1 looks like
Column1 Column2
1 a
1 b
1 c
2 w
2 e
3 x
I need my Sql statment to output as following
Column1 Column2
1 a, b, c
2 w, e
3 x
So in other words I need to group by column1 and have the column2 values concatenate with comma seperated. Please note this will need to be able to run on SQL Server 2000 and above

You can create a function to concat the values
create function dbo.concatTable1(#column1 int) returns varchar(8000)
as
begin
declare #output varchar(8000)
select #output = coalesce(#output + ', ', '') + column2
from table1
where column1 = #column1 and column2 > ''
return #output
end
GO
So assuming you had this table
create table table1 (column1 int, column2 varchar(10))
insert table1 select 1, 'a'
insert table1 select 1, 'b'
insert table1 select 1, 'c'
insert table1 select 2, 'w'
insert table1 select 2, 'e'
insert table1 select 3, 'x'
GO
You use it like this
select column1, dbo.concatTable1(column1) column2
from table1
group by column1

Related

Compare 2 different string columns in an SQL database

I have two different columns of strings and I want to check if one is contained in the other and create a third column which has values of either 1 or 0( 1 if column2 contains column 1 and 0 if otherwise)
For example:
Column1 Column2 Column3
Spar I am Sparta 1
How are you Sparta 0
How do you do this string comparison in SQL ?
You could use the charindex function. Assuming you just want to check if colum1 is contained in column2:
SELECT CASE WHEN CHARINDEX(column1, column2) > 0
THEN 1
ELSE 0
END AS column3
FROM mytable
If you need to check both ways, just add another call:
SELECT CASE WHEN CHARINDEX(column1, column2) > 0 OR
CHARINDEX(column2, column1) > 0
THEN 1
ELSE 0
END AS column3
FROM mytable
Try this:
WITH temp(col1, col2) AS(
SELECT 'Spar', 'I am Sparta' UNION ALL
SELECT 'How are you', 'Sparta'
)
SELECT
*,
col3 =
CASE
WHEN col1 like '%' + col2 + '%' or col2 like '%' + col1 + '%' THEN 1
ELSE 0
END
FROM temp
It should work
select column1,column2, (case when column2 like '%' + column1 + '%' then 1 else 0 end) as column3 FROM yourtable
Try this
DECLARE #table1 TABLE
(
Column1 VARCHAR(20),
Column2 VARCHAR(20)
)
INSERT INTO #table1
VALUES ('Spar',
'I am Sparta'),
('How are you',
'Sparta')
SELECT column1,
column2,
CASE
WHEN CHARINDEX(column1, column2) > 0
OR CHARINDEX(column1, column2) > 0 THEN 1
ELSE 0
END AS column3
FROM #table1

how to parse a string to return only numbers meeting certain criteria in Oracle

I am trying to locate numbers within views where the text of the view is in a long format. I have already been able to convert the long to a char.
I have been messing around with replace and instr functions but haven't been able to get it out neatly.
Ideally I would like to populate a table with the following information.
the user (grantee) of the view, the ID values that are in the file, 1 row for each ID.
A sample view would be something like this.
Select column1, column2, column3 from table_with_data where ID in (1,5,322,54,12)
or
Select column1, column2, column3 from table_with_data where ID = 2
How could I create 5 rows in a table just pulling the numeric values here in the first case and just 1 for the second?
This might get you going:
CREATE TABLE SampleData (userName, viewString) AS SELECT * FROM (
SELECT 'auser', 'Select column1, column2, column3 from table_with_data where ID in (1,5,322,54,12)' FROM DUAL UNION ALL
SELECT 'buser', 'Select column1, column2, column3 from table_with_data where ID in (15,3,22,5,412)' FROM DUAL UNION ALL
SELECT 'cuser', 'Select column1, column2, column3 from table_with_data where ID = 2 ' FROM DUAL UNION ALL
SELECT 'duser', 'Select column1, column2, column3 from table_with_data where ID = 45' FROM DUAL
);
WITH
MultipleValues (userName, ids) AS (
SELECT /* MATERIALIZE */
userName
, ',' || SUBSTR(viewString, INSTR(viewString, '(') + 1, LENGTH(viewString) - INSTR(viewString, '(') - 1) || ', '
FROM SampleData
WHERE INSTR(viewString, '(') > 1
)
SELECT
userName
, TO_NUMBER(SUBSTR(ids, INSTR(ids, ',', 1, lvl) + 1, INSTR(ids, ',', 1, lvl + 1) - INSTR(ids, ',', 1, lvl) - 1)) id
FROM
(SELECT userName, ids FROM MultipleValues),
(SELECT LEVEL AS lvl FROM DUAL CONNECT BY LEVEL <= 100)
WHERE lvl <= LENGTH(ids) - LENGTH(REPLACE(ids, ',')) - 1
UNION ALL
SELECT /* MATERIALIZE */
userName
, TO_NUMBER(REGEXP_SUBSTR(TRIM(viewString), '(\d+)$'))
FROM SampleData
WHERE INSTR(viewString, '=') > 1
ORDER BY userName, id;
See it running: SQL Fiddle
Please comment, or extend your question if this needs further detail / adjustment.
You can try inserting into view, which will act as check option for insert statement
Lets say table has four column like below
CREATE TABLE table_with_data
(ID NUMBER,column1 VARCHAR2(100), column2 VARCHAR2(100), column3 VARCHAR2(100));
and view as below
CREATE OR REPLACE VIEW SAMP_VIEW
AS
Select * from table_with_data where ID = 2
After creation we can try inserting into view as below
INSERT INTO SAMP_VIEW values(2,'hello','hello','hello');
if you try insert other than id = 2 then it will throw
ORA-01402: view WITH CHECK OPTION where-clause violation
I hope this will help you

select distinct elements from two columns with comma separated in sql

I have a table with two columns like this
col1 col2
a b
b a
c d
d a
I want to get distinct values of these two columns combined with comma separated.
Expected out put is like this
a,b,c,d
The following example concatenate row values into a variable
DECLARE #val nvarchar(max)
SELECT #val = COALESCE(#val + ',' + col1, col1)
FROM (SELECT col1
FROM dbo.twoColumns
UNION
SELECT col2
FROM dbo.twoColumns
) x
SELECT #val
Demo on SQLFiddle
try this , its very much easy i think
select group_concat(distinct(c)) as d
from
(
select col1 c from your_table
union
select col2 c from your_table
) as d

Count the Null columns in a row in SQL

I was wondering about the possibility to count the null columns of row in SQL, I have a table Customer that has nullable values, simply I want a query that return an int of the number of null columns for certain row(certain customer).
This method assigns a 1 or 0 for null columns, and adds them all together. Hopefully you don't have too many nullable columns to add up here...
SELECT
((CASE WHEN col1 IS NULL THEN 1 ELSE 0 END)
+ (CASE WHEN col2 IS NULL THEN 1 ELSE 0 END)
+ (CASE WHEN col3 IS NULL THEN 1 ELSE 0 END)
...
...
+ (CASE WHEN col10 IS NULL THEN 1 ELSE 0 END)) AS sum_of_nulls
FROM table
WHERE Customer=some_cust_id
Note, you can also do this perhaps a little more syntactically cleanly with IF() if your RDBMS supports it.
SELECT
(IF(col1 IS NULL, 1, 0)
+ IF(col2 IS NULL, 1, 0)
+ IF(col3 IS NULL, 1, 0)
...
...
+ IF(col10 IS NULL, 1, 0)) AS sum_of_nulls
FROM table
WHERE Customer=some_cust_id
I tested this pattern against a table and it appears to work properly.
My answer builds on Michael Berkowski's answer, but to avoid having to type out hundreds of column names, what I did was this:
Step 1: Get a list of all of the columns in your table
SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 'myTable';
Step 2: Paste the list in Notepad++ (any editor that supports regular expression replacement will work). Then use this replacement pattern
Search:
^(.*)$
Replace:
\(CASE WHEN \1 IS NULL THEN 1 ELSE 0 END\) +
Step 3: Prepend SELECT identityColumnName, and change the very last + to AS NullCount FROM myTable and optionally add an ORDER BY...
SELECT
identityColumnName,
(CASE WHEN column001 IS NULL THEN 1 ELSE 0 END) +
-- ...
(CASE WHEN column200 IS NULL THEN 1 ELSE 0 END) AS NullCount
FROM
myTable
ORDER BY
NullCount DESC
For ORACLE-DBMS only.
You can use the NVL2 function:
NVL2( string1, value_if_not_null, value_if_null )
Here is a select with a similiar approach as Michael Berkowski suggested:
SELECT (NVL2(col1, 0, 1)
+ NVL2(col2, 0, 1)
+ NVL2(col3, 0, 1)
...
...
+ NVL2(col10, 0, 1)
) AS sum_of_nulls
FROM table
WHERE Customer=some_cust_id
A more generic approach would be to write a PL/SQL-block and use dynamic SQL. You have to build a SELECT string with the NVL2 method from above for every column in the all_tab_columns of a specific table.
Unfortunately, in a standard SQL statement you will have to enter each column you want to test, to test all programatically you could use T-SQL. A word of warning though, ensure you are working with genuine NULLS, you can have blank stored values that the database will not recognise as a true NULL (I know this sounds strange).
You can avoid this by capturing the blank values and the NULLS in a statement like this:
CASE WHEN col1 & '' = '' THEN 1 ELSE 0 END
Or in some databases such as Oracle (not sure if there are any others) you would use:
CASE WHEN col1 || '' = '' THEN 1 ELSE 0 END
You don't state RDBMS. For SQL Server 2008...
SELECT CustomerId,
(SELECT COUNT(*) - COUNT(C)
FROM (VALUES(CAST(Col1 AS SQL_VARIANT)),
(Col2),
/*....*/
(Col9),
(Col10)) T(C)) AS NumberOfNulls
FROM Customer
Depending on what you want to do, and if you ignore mavens, and if you use SQL Server 2012, you could to it another way. .
The total number of candidate columns ("slots") must be known.
1. Select all the known "slots" column by column (they're known).
2. Unpivot that result to get a
table with one row per original column. This works because the null columns don't
unpivot, and you know all the column names.
3. Count(*) the result to get the number of non-nulls;
subtract from that to get your answer.
Like this, for 4 "seats" in a car
select 'empty seats' = 4 - count(*)
from
(
select carId, seat1,seat2,seat3,seat4 from cars where carId = #carId
) carSpec
unpivot (FieldValue FOR seat in ([seat1],[seat2],[seat3],[seat4])) AS results
This is useful if you may need to do more later than just count the number of non-null columns, as it gives you a way to manipulate the columns as a set too.
This will give you the number of columns which are not null. you can apply this appropriately
SELECT ISNULL(COUNT(col1),'') + ISNULL(COUNT(col2),'') +ISNULL(COUNT(col3),'')
FROM TABLENAME
WHERE ID=1
The below script gives you the NULL value count within a row i.e. how many columns do not have values.
{SELECT
*,
(SELECT COUNT(*)
FROM (VALUES (Tab.Col1)
,(Tab.Col2)
,(Tab.Col3)
,(Tab.Col4)) InnerTab(Col)
WHERE Col IS NULL) NullColumnCount
FROM (VALUES(1,2,3,4)
,(NULL,2,NULL,4)
,(1,NULL,NULL,NULL)) Tab(Col1,Col2,Col3,Col4) }
Just to demonstrate I am using an inline table in my example.
Try to cast or convert all column values to a common type it will help you to compare the column of different type.
I haven't tested it yet, but I'd try to do it using a PL\SQL function
CREATE OR REPLACE TYPE ANYARRAY AS TABLE OF ANYDATA
;
CREATE OR REPLACE Function COUNT_NULL
( ARR IN ANYARRAY )
RETURN number
IS
cnumber number ;
BEGIN
for i in 1 .. ARR.count loop
if ARR(i).column_value is null then
cnumber := cnumber + 1;
end if;
end loop;
RETURN cnumber;
EXCEPTION
WHEN OTHERS THEN
raise_application_error
(-20001,'An error was encountered - '
||SQLCODE||' -ERROR- '||SQLERRM);
END
;
Then use it in a select query like this
CREATE TABLE TEST (A NUMBER, B NUMBER, C NUMBER);
INSERT INTO TEST (NULL,NULL,NULL);
INSERT INTO TEST (1 ,NULL,NULL);
INSERT INTO TEST (1 ,2 ,NULL);
INSERT INTO TEST (1 ,2 ,3 );
SELECT ROWNUM,COUNT_NULL(A,B,C) AS NULL_COUNT FROM TEST;
Expected output
ROWNUM | NULL_COUNT
-------+-----------
1 | 3
2 | 2
3 | 1
4 | 0
This is how i tried
CREATE TABLE #temptablelocal (id int NOT NULL, column1 varchar(10) NULL, column2 varchar(10) NULL, column3 varchar(10) NULL, column4 varchar(10) NULL, column5 varchar(10) NULL, column6 varchar(10) NULL);
INSERT INTO #temptablelocal
VALUES (1,
NULL,
'a',
NULL,
'b',
NULL,
'c')
SELECT *
FROM #temptablelocal
WHERE id =1
SELECT count(1) countnull
FROM
(SELECT a.ID,
b.column_title,
column_val = CASE b.column_title
WHEN 'column1' THEN a.column1
WHEN 'column2' THEN a.column2
WHEN 'column3' THEN a.column3
WHEN 'column4' THEN a.column4
WHEN 'column5' THEN a.column5
WHEN 'column6' THEN a.column6
END
FROM
( SELECT id,
column1,
column2,
column3,
column4,
column5,
column6
FROM #temptablelocal
WHERE id =1 ) a
CROSS JOIN
( SELECT 'column1'
UNION ALL SELECT 'column2'
UNION ALL SELECT 'column3'
UNION ALL SELECT 'column4'
UNION ALL SELECT 'column5'
UNION ALL SELECT 'column6' ) b (column_title) ) AS pop WHERE column_val IS NULL
DROP TABLE #temptablelocal
Similary, but dynamically:
drop table if exists myschema.table_with_nulls;
create table myschema.table_with_nulls as
select
n1::integer,
n2::integer,
n3::integer,
n4::integer,
c1::character varying,
c2::character varying,
c3::character varying,
c4::character varying
from
(
values
(1,2,3,4,'a','b','c','d'),
(1,2,3,null,'a','b','c',null),
(1,2,null,null,'a','b',null,null),
(1,null,null,null,'a',null,null,null)
) as test_records(n1, n2, n3, n4, c1, c2, c3, c4);
drop function if exists myschema.count_nulls(varchar,varchar);
create function myschema.count_nulls(schemaname varchar, tablename varchar) returns void as
$BODY$
declare
calc varchar;
sqlstring varchar;
begin
select
array_to_string(array_agg('(' || trim(column_name) || ' is null)::integer'),' + ')
into
calc
from
information_schema.columns
where
table_schema in ('myschema')
and table_name in ('table_with_nulls');
sqlstring = 'create temp view count_nulls as select *, ' || calc || '::integer as count_nulls from myschema.table_with_nulls';
execute sqlstring;
return;
end;
$BODY$ LANGUAGE plpgsql STRICT;
select * from myschema.count_nulls('myschema'::varchar,'table_with_nulls'::varchar);
select
*
from
count_nulls;
Though I see that I didn't finish parametising the function.
My answer builds on Drew Chapin's answer, but with changes to get the result using a single script:
use <add_database_here>;
Declare #val Varchar(MAX);
Select #val = COALESCE(#val + str, str) From
(SELECT
'(CASE WHEN '+COLUMN_NAME+' IS NULL THEN 1 ELSE 0 END) +' str
FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = '<add table name here>'
) t1 -- getting column names and adding the case when to replace NULLs for zeros or ones
Select #val = SUBSTRING(#val,1,LEN(#val) - 1) -- removing trailling add sign
Select #val = 'SELECT <add_identity_column_here>, ' + #val + ' AS NullCount FROM <add table name here>' -- adding the 'select' for the column identity, the 'alias' for the null count column, and the 'from'
EXEC (#val) --executing the resulting sql
With ORACLE:
Number_of_columns - json_value( json_array( comma separated list of columns ), '$.size()' ) from your_table
json_array will build an array with only the non null columns and the json_query expression will give you the size of the array
There isn't a straightforward way of doing so like there would be with counting rows. Basically, you have to enumerate all the columns that might be null in one expression.
So for a table with possibly null columns a, b, c, you could do this:
SELECT key_column, COALESCE(a,0) + COALESCE(b,0) + COALESCE(c,0) null_col_count
FROM my_table

How to format TSQL SELECT output in SQL Sever

How to loop through a select statement results to have a formatted text?
for example the select is like:
select name from table
and we want a variable #names like this:
"name1,name2,name3"
Database is SQL Server 2005
If table contains several records, i.e.:
1, name1, ..
2, name2, ..
3, name3, ..
then this query:
DECLARE #names VARCHAR(MAX)
SELECT #names = COALESCE(#names + ', ', '') + name
FROM table
will produce next result:
name1, name2, name3
See COALESCE on MSDN
This would need to be done within a function. There's no quick way to do this. Normally you would do this within your client side code.
If you plan on making a function that you do on each row form another query it will be really slow, because the function needs to be called for each row. You should work with sets of data at one time, it does all rows at one time. This is an example of how to concatenate multiple values for each row of another query:
set nocount on;
declare #t table (id int, name varchar(20), x char(1))
insert into #t (id, name, x)
select 1,'test1', 'a' union
select 1,'test1', 'b' union
select 1,'test1', 'c' union
select 2,'test2', 'a' union
select 2,'test2', 'c' union
select 3,'test3', 'b' union
select 3,'test3', 'c'
SELECT p1.id, p1.name,
stuff(
(SELECT
', ' + x
FROM #t p2
WHERE p2.id=p1.id
ORDER BY name, x
FOR XML PATH('')
)
,1,2, ''
) AS p3
FROM #t p1
GROUP BY
id, name
OUTPUT:
id name p3
----------- -------------------- -----------
1 test1 a, b, c
2 test2 a, c
3 test3 b, c