I have a table with schema like below:
root
|id
|name
|col1
|col2
|...
|col30
Conditions are that multiple rows can have the same name (they're not primary key - the key is the ID). Values in col1-col30 will be some string, or it can have the string "null".
I'm interested in the number of columns filled in for each name.
For example,
if name "test1" has col1-5 filled in a row, and another row has "test1" and have col1, 3, 10, 6 filled in (and the rest of unfilled columns are just string value "null"), "test1" should have value 9.
I'm pretty new to SQL and have been looking this up.. Please help.
Give this a try:
SELECT
name,
CASE WHEN col1_max IS NOT NULL THEN 1 ELSE 0 END + -- Only include non-NULL values
CASE WHEN col2_max IS NOT NULL THEN 1 ELSE 0 END
FROM (
SELECT
name,
MAX(col1) AS col1_max, -- Non-NULL values come before NULL
MAX(col2) AS col2_max
FROM MyTable
GROUP BY name
) src
You can add more the rest of the columns to fit your case.
Updated
I just realized your NULL case is with a "null" string. Modified:
SELECT
name,
CASE WHEN col1_max IS NOT NULL THEN 1 ELSE 0 END + -- Only include non-NULL values
CASE WHEN col2_max IS NOT NULL THEN 1 ELSE 0 END
FROM (
SELECT
name,
MAX(CASE WHEN col1 = 'null' THEN NULL ELSE col1 END) AS col1_max, -- Non-NULL values come before NULL
MAX(CASE WHEN col2 = 'null' THEN NULL ELSE col2 END) AS col2_max
FROM MyTable
GROUP BY name
) src
First you unpivot your table and count those rows that have not null values. In postgres, you can achieve this with unnest. I have only used col1..7 -- change to upto col30 in your case
WITH t AS(
SELECT id,name,
unnest(array['col1', 'col2', 'col3', 'col4', 'col5', 'col6', 'col7']) AS colname,
unnest(array[col1, col2, col3, col4, col5, col6, col7]) AS colvalue
FROM your_table)
SELECT id, name,
SUM(CASE WHEN colvalue IS NULL THEN 0 ELSE 1 END) AS count_filled
FROM t
GROUP BY 1,2;
Related
I have table like below:
I want result like even one column is null or empty string those records should not be display.
I want results like below:
select * from WHERE col1 IS NOT NULL AND col2 IS NOT NULL AND col3 IS NOT NULL AND col4 IS NOT NULL AND col5 IS NOT NULL....AND col9 IS NOT NULL
If you wanted an efficient way to do this, then one method would be a persistent computed column:
alter table t
add numNulls as ( (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) +
. . .
) persisted;
You can index this column:
create index t_numNulls on t(numNulls);
And then you can use this in the select:
select t.*
from t
where num_nulls = 0;
That said, I suspect that your real problem is the data model. I am guessing that those 90 columns are really an "array" -- that is, all the same entity. These should be implemented as separate rows in a junction table.
I have table like followed by
User Col1 Col2
ABC 35 75
ABC 500 75
and I need the select query for following output.
User Col1 Col2 Result
ABC 35 75 40
ABC 500 75 115
Check col1<col2 then Result Col2-Col1
else
Col2 + Result(Row1)
Ex:
35<75 then 40
else
75+40
I don't know how to start please help us for output..
Thanks in advance
What I have tried
Select User,Col1,Col2
,Case When Col1<Col2 then Col2-Col1 Else Col2 End as Result
Then i got the output like followed by
User Col1 Col2 Result
ABC 35 75 40
ABC 500 75 **75**
I need 115 instead of 75 in Result of second row
CREATE TABLE #tmp
(
UserName VARCHAR(10),
col1 INT,
col2 INT
)
INSERT INTO #tmp (UserName, col1, col2)
SELECT 'ABC', 35, 75
UNION ALL SELECT 'ABC', 500, 75
SELECT tmp.UserName
,tmp.col1
,tmp.col2
,CASE WHEN tmp.Result = 0 THEN tmp.col2 + LAG(tmp.Result) OVER (ORDER BY (SELECT NULL)) ELSE tmp.Result END AS Result
FROM
(
Select UserName,Col1,Col2
,Case When Col1<Col2 then Col2-Col1 Else 0 End as Result
FROM #tmp
)tmp
You could try something like this:
SELECT *
,Case When Col1 < Col2 then Col2-Col1
Else (Col2 + LAG(Col2-Col1 ,1) OVER(ORDER BY Col2)) --Order by
End as Result
FROM TableName
By using LAG you can access the previous row of data. Then all I do is add the previous result to the Col2 value.
Else (Col2 + LAG(Col2-Col1 ,1) OVER(ORDER BY Col2))
Important note below.
The only thing to note here is the ORDER BY Col2. At the moment that will work for just the two rows of data. But, if you have more you should add some sort of auto incrementing ID field to keep the order the same as how you enter the data. Then you would change it to ORDER BY ID.
You can try below using lag() function
Select User,Col1,Col2
,Case When Col1<Col2 then Col2-Col1
Else Col2+lag(col2-col1) over(partition by user order by col2 ) End as Result
from tablename
You need to use the LAG function for this.here is the code...if you like the answer please vote.
Select *
,Case When Col1<Col2 then Col2-Col1
Else Col2+lag(col2-col1) over(partition by user order by col2 ) End as Result
from review
First, you need a column that represents the ordering of the rows.
If I generalize the problem, then you need the groups defined by col1 < col2. You can define the groups by taking a cumulative sum of when col1 < col2.
The actual calculation is then a cumulative sum over the group:
select t.*,
(case when col1 < col2 then col2 - col1
else sum(case when col1 > col2 then col2 - col1 else col2 end) over (partition by user, grp order by ?)
end) as result
from (select t.*,
sum(case when col1 < col2 then 1 else 0 end) over (partition by user order by ?) as grp
from t
) t
from t;
The ? is for the column that specifies the ordering of the rows.
I would like to get to know how I can get the first column with NULL value from the left for each row in my table, I've tried with SELECT CASE but it doesn't work the way I would like.
Guys, I'd like to be crystal-clear about what I want to accomplish. I have a table with 22 columns and there are rows in which last 10 columns have NULL values but I need to get to know only a name of the first column from the left with NULL value.
You get the value from the first non-NULL column using coalesce():
select coalesce(col1, col2, col3, . . .)
You can get the name using case logic:
select (case when col1 is not null then 'col1'
when col2 is not null then 'col2'
. . .
end)
Just specify NULL as your first field selection.
SELECT NULL, FieldA, FieldB, FieldC etc
FROM yourtable
The only general approach here is case statement:
Case
when col1 is null then 'col1'
when col2 is null then 'col2'
when col3 is null then 'col3'
end as frst_null
This way frst_null would contain the name of the first column containing Null value. You can order columns whichever order you like.
Lets say I have the following table depicting a one-many relationship
col1 | col2
-------------
1 | foo
1 | bar
2 | foo
3 | buzz
I need to group by col1 and I need a boolean indicating whether or not there is both a mapping to 'foo' and a mapping to 'bar'.
So, the final result set would be
col1 | foobar
-------------
1 | 1
2 | 0
3 | 0
What is the best way to achieve this in T-SQL?
I've been trying something roughly equivalent to the following query with no luck.
SELECT
col1
, (
MAX (
CASE WHEN
COL2 = 'foo'
THEN 1 ELSE 0
END) = 1
AND
MAX (
CASE WHEN
COL2 = 'bar'
THEN 1 ELSE 0
END) = 1
)
FROM
table
GROUP BY
col1
EDIT:
To clarify, this table is a simplification.
I am looking for a solution to the general problem of having a one-many mapping and needing to produce a new 1-1 mapping with a Boolean indicating if a variable number of predicates are true of the different elements in the groups in the co-domain. (grouped by the fact that the same element maps to them)
Also, I should clarify that these various predicates could be anything.
For example, maybe I want to see if at least one of the columns in one of the rows = 'foo' and also that a different column in a (possibly different) row within the same group is between a certain set of numeric values.
What about this?
EDIT: Better use COUNT(DISTINCT col2)
DECLARE #tbl TABLE(col1 INT,col2 VARCHAR(100));
INSERT INTO #tbl VALUES
(1,'foo')
,(1,'bar')
,(2,'foo')
,(3,'buzz');
SELECT col1
,COUNT(DISTINCT col2)-1
FROM #tbl
GROUP BY col1
UPDATE:
If you try it like this, you would even see, which values are there. If you are only interested in "one or many" you might check for a comma in the returned string:
DECLARE #tbl TABLE(col1 INT,col2 VARCHAR(100));
INSERT INTO #tbl VALUES
(1,'foo')
,(1,'bar')
,(2,'foo')
,(3,'buzz');
SELECT outerTbl.col1
,STUFF
(
(
SELECT DISTINCT ', ' + col2
FROM #tbl AS innerTbl
WHERE innerTbl.col1=outerTbl.col1
FOR XML PATH('')
),1,2,''
)
FROM #tbl AS outerTbl
GROUP BY outerTbl.col1
This is the result:
1 bar, foo
2 foo
3 buzz
Here is a simple option
select col1
,MAX(case when col2='foo' then 1 else 0 end)*MAX(case when col2='bar' then 1 else 0 end) foobar
from #tbl
group by col1
Try this:
select
col1,
case when foo > 0 and bar > 0 then true else false end foobar
from (
select
col1,
sum(case when col2 = 'foo' then 1 end) foo,
sum(case when col2 = 'bar' then 1 end) bar
from table
group by col1) x
I have a table Like this
Col1 | Col2
-----------
a | d
b | e
c | a
Now I want to create an statement to get an output like this:
First| Second
-------------------
a | Amsterdamm
b | Berlin
c | Canada
...
So far I have this consturct what is not working
SELECT *
FROM(
SELECT DISTINCT
CASE
when Col1 IS NULL then 'NA'
else Col1
END
FROM Table1
UNION
SELECT DISTINCT
CASE
when Col2 IS NULL then 'NA'
else Col2
END
FROM Table1
) AS First
,
(
SELECT DISTINCT
when First= 'a' then 'Amsterdam'
when First= 'b' then 'Berlin'
when First= 'c' then 'Canada'
) AS Second
;
can you help me with that
Sorry I have to edit my question to be more specific.
Not as familiar with DB2... I'll lookup if it has a concat function in a sec... and it does.
SELECT First, case when first = 'a' then
concat('This is a ',first)
case when first = 'b' then
concat('To Be or not to ',first)
case else
concat('This is a ',first) end as Second
FROM (
SELECT coalesce(col1, 'NA') as First
FROM Table
UNION
SELECT coalesce(col2, 'NA')
FROM table) SRC
WHERE first <> 'NA'
What this does is generate a single inline view called src with a column called first. If col1 or col2 of table are null then it substitutes NA for that value. It then concatenates first and the desired text excluding records with a first value of 'NA'
Or if you just create an inline table with the desired values and join in...
SELECT First, x.b as Second
FROM (
SELECT coalesce(col1, 'NA') as First
FROM Table
UNION
SELECT coalesce(col2, 'NA')
FROM table) SRC
INNER JOIN (select a,b
from (values ('a', 'This is a'),
('b', 'To B or not to' ),
('c', 'I like cat whose name starts with')) as x(a,b)) X;
on X.a = src.first
WHERE first <> 'NA'
Personally I find the 2nd option easier to read. Though if you have meaning for a,b,c I would think you'd want that stored in a table somewhere for additional access. In code seems like a bad place to store data like this that could change.
Assuming you want
a this is a a
b this is a b
c this is a c
d this is a d
e this is a e
thanks to xQbert
I could solve this problem like this
SELECT FirstRow, concat
(
CASE FirstRow
WHEN 'AN' then 'amerstdam'
WHEN 'G' then 'berlin'
ELSE 'NA'
END, ''
) AS SecondRow
FROM(
Select coalesce (Col1, 'NA') as FirstRow
FROM Table1
UNION
Select coalesce (Col2, 'NA')
FROM Table1) SRC
WHERE FirstRow <> 'NA'
;