T-SQL Replacing Nulls with Empty String - tsql

I have the following union query:
select
'type1' as type,
id,
add_date
from Table A
where type = type1
union all
select
'type2' as type,
id,
'' as add_date
from Table B
Since add_date isn't applicable to type 2, it is providing 1900-01-01 00:00:00.000 for any records returned. I could change the statement from '' as add_date to NULL as add_date, but the user is asking if I can remove the null from the report and leave the result as an empty string where applicable. What is the best way to accomplish this? Thanks.!

You can use null and cast the result as a string with the ISNULL function applied:
SELECT [type], id, ISNULL(CAST(add_date as VARCHAR), '') AS add_date
FROM (
SELECT 'type1' as [type]
,id
,add_date
FROM TableA
UNION
SELECT 'type2' as [type]
,id
,null as add_date
FROM TableB
) inside_query
This will allow you to provide blanks instead of 1/1/1900 or NULL keyword. You can also change the CAST to a CONVERT if you need to provide specific date formatting.

try this:
select
'type1' as type,
id,
CAST(add_date AS VARCHAR) as add_date
from Table A
where type = type1
union all
select
'type2' as type,
id,
'' as add_date
from Table B

Related

add Exists operator in a union sql query

I have two tables that am using union to combine the result-set of them my problem here is Each SELECT statement within UNION must have the same number of columns and data types, I don't have the same number of columns so am creating null columns
select
d.deal_id as order_id,
EXISTS(select * from table1 c where d.user_id = c.user_id) as IsUser, --this returns boolean value
from table 1 c
union
select
cast(o.id as varchar) as order_id,
coalesce('No_user'::text,'0'::text) as IsUser, --i get an error :UNION types boolean and character varying cannot be matched
from table2 o
how can I create a null column in table2 that matches the boolean data type of the table1
how can I create a null column in table2 that matches the boolean data type of the table1
By putting NULL into the SELECT list of the second query.
Generally (in SQL) the datatype for the column is dictated by the first query in the union set and other queries must match. You don't need column aliases for subsequent queries either, but they may help make a query more readable:
select
d.deal_id as order_id,
EXISTS(select * from table1 c where d.user_id = c.user_id) as IsUser, --this returns boolean value
from table 1 c
union
select
cast(o.id as varchar) as order_id,
null --IsUser
from table2 o
If you have a case where the type of the column in the first query is different to what you want in the output, you cast the first column:
select 1::boolean as boolcol
UNION
select true
This will ensure that the column is boolean, rather than giving a "integer and bool cannot be matched". Remember also that, should you need to, NULL can be cast to a type, e.g. select null::boolean as bolcol

How to make the number of columns same using 'Union' in postgresql?

Here is the code:
SELECT 0 customer_id, 0 store_id, '' first_name, '' last_name,
'' email, 0 address_id, activebool, create_date, last_update, 0 active, * FROM country
UNION ALL
SELECT *, 0 country_id, '' country, last_update FROM customer
I don't know how to do that for certain data types like boolean type and date/time type like date and timestamp. I'm new to postgresql. So can anyone tell me what values should I put for the data types I mentioned? Sorry for my english.
I hope this can help.
I Tried those on psql they work fine:
For timestamp
SELECT '-infinity' AS "test" UNION SELECT '0001-01-01 00:00:00'::timestamp;
For boolean
SELECT null AS "test" UNION SELECT 0::boolean;
SELECT 'true' AS "test" UNION SELECT 0::boolean;

TSQL challenge to join and union in one step, is it pssble?

I'm trying to make single datasource which will have several data types which later will be ID'ed in our BI application (and I can NOT change anything), each type ID'ed by column Type like in example below, so basically I have 2 tables which I want join/union at the same time, I tried to play with join, and could not make it work so fare. Do you think it's doable ?? THere is some dynamic going on in input tables so it's hard to go with preset columns which will serve all tables.
This is my input:
;WITH cte1 AS (SELECT 'A' type, 'alpha1' rec1, 'bravo1' rec2 UNION
SELECT 'A' type, 'alpha2' rec1, 'bravo2' rec2 UNION
SELECT 'A' type, 'alpha3' rec1, 'bravo3' rec2 )
, cte2 AS (
SELECT 'X' type, 'zulu01' recX UNION
SELECT 'X' type, 'zulu02' rexX )
and this is wanted output
;WITH cteWanted AS (SELECT 'A' type, 'alpha1' rec1, 'bravo1' rec2, NULL recX UNION
SELECT 'A' type, 'alpha2' rec1, 'bravo2' rec2, NULL recX UNION
SELECT 'A' type, 'alpha3' rec1, 'bravo3' rec2, NULL recX UNION
SELECT 'X' type, NULL rec1, NULL rec2, 'zulu01' recX UNION
SELECT 'X' type, NULL rec1, NULL rec2, 'zulu02' recX )
SELECT * FROM cteTarget
Tx all, I realized that I need to go with HABO solution.

How to solve type mismatch in CTE when a NULL anchor recurs on NOT NULL fields

In a SQL script which is due to end up feeding a SSRS RDL document, I'm trying to leverage CTEs to recursively fetch my data.
The actual SQL code is way too big to copypaste here but I made up this short repro:
declare #temp table
(
id uniqueidentifier not null default(newid()),
name varchar(100) not null default('new'),
value varchar(100) not null default('newValue'),
parentid uniqueidentifier null
)
insert into #temp (id, name, value, parentid) values ('12312312-1234-1234-1234-123412341234', 'o1', 'val1', null)
insert into #temp (id, name, value, parentid) values ('12312312-1234-1234-1234-123412341235', 'o2', 'val2', '12312312-1234-1234-1234-123412341234')
insert into #temp (id, name, value, parentid) values ('12312312-1234-1234-1234-123412341236', 'o3', 'val3', '12312312-1234-1234-1234-123412341234')
;with
cte(id,name, value, pid, pname, pvalue) as (
select id, name, value, null, null, null from #temp where parentid is null
union all
select r.id, r.name, r.value, a.id, a.name, a.value
from #temp r inner join cte a
on a.id = r.parentid
)
select * from cte
The CTE errors out like this (tested in SSMS 2012):
Msg 240, Level 16, State 1, Line 13
Types don't match between the anchor and the recursive part in column "pid" of recursive query "cte".
Msg 240, Level 16, State 1, Line 13
Types don't match between the anchor and the recursive part in column "pname" of recursive query "cte".
Msg 240, Level 16, State 1, Line 13
Types don't match between the anchor and the recursive part in column "pvalue" of recursive query "cte".
I'm pretty sure this is due to the NULL values being selected in the anchor part as opposed to the NOT NULL fields fetched in the recursive part... Can this issue be worked around ?
(I'm open to alternative ways of accomplishing this task.)
Cast the literal null values to match.
;with
cte(id,name, value, pid, pname, pvalue) as (
select id, name, value, cast(null as uniqueidentifier),
cast(null as varchar(100)), cast(null as varchar(100))
from #temp where parentid is null
union all
select r.id, r.name, r.value, a.id, a.name, a.value
from #temp r inner join cte a
on a.id = r.parentid
)
Otherwise SQL will assume the default type int for each null which won't match the second part of the union.

T-SQL GROUP BY over a dynamic list

I have a table with an XML type column. This column contains a dynamic list of attributes that may be different between records.
I am trying to GROUP BY COUNT over these attributes without having to go through the table separately for each attribute.
For example, one record could have attributes A, B and C and the other would have B, C, D then, when I do the GROUP BY COUNT I would get A = 1, B = 2, C = 2 and D = 1.
Is there any straightforward way to do this?
EDIT in reply to Andrew's answer:
Because my knowledge of this construct is superficial at best I had to fiddle with it to get it to do what I want. In my actual code I needed to group by the TimeRange, as well as only select some attributes depending on their name. I am pasting the actual query below:
WITH attributes AS (
SELECT
Timestamp,
N.a.value('#name[1]', 'nvarchar(max)') AS AttributeName,
N.a.value('(.)[1]', 'nvarchar(max)') AS AttributeValue
FROM MyTable
CROSS APPLY AttributesXml.nodes('/Attributes/Attribute') AS N(a)
)
SELECT Datepart(dy, Timestamp), AttributeValue, COUNT(AttributeValue)
FROM attributes
WHERE AttributeName IN ('AttributeA', 'AttributeB')
GROUP BY Datepart(dy, Timestamp), AttributeValue
As a side-note: Is there any way to reduce this further?
WITH attributes AS (
SELECT a.value('(.)[1]', 'nvarchar(max)') AS attribute
FROM YourTable
CROSS APPLY YourXMLColumn.nodes('//path/to/attributes') AS N(a)
)
SELECT attribute, COUNT(attribute)
FROM attributes
GROUP BY attribute
CROSS APPLY is like being able to JOIN the xml as a table. The WITH is needed because you can't have xml methods in a group clause.
Here is a way to get the attribute data into a way that you can easily work with it and reduce the number of times you need to go through the main table.
--create test data
declare #tmp table (
field1 varchar(20),
field2 varchar(20),
field3 varchar(20))
insert into #tmp (field1, field2, field3)
values ('A', 'B', 'C'),
('B', 'C', 'D')
--convert the individual fields from seperate columns to one column
declare #table table(
field varchar(20))
insert into #table (field)
select field1 from #tmp
union all
select field2 from #tmp
union all
select field3 from #tmp
--run the group by and get the count
select field, count(*)
from #table
group by field