Joining two select queries from the same table - sql

The table contains an ID column, valueHeading column and a value column. I want to separate the value column into two new columns called valueHeading1 and valueHeading2 depending on which type of valueHeading the value has.
So I want to join this select:
Edit: Full join
SELECT ID
,valueHeading
,value as 'valueHeading1'
FROM table1
WHERE valueHeading = 'valueHeading1'
With This select:
SELECT ID
,value as 'valueHeading2'
FROM table1
WHERE valueHeading = 'valueHeading2'
on their respective ID's. How do I do this?
Edit to illustrate what I want to do:
Original table:
ID valueHeading value
0 valueHeading1 a
0 valueHeading2 a
1 valueHeading1 ab
1 valueHeading2 NULL
2 valueHeading1 abcd
2 valueHeading2 abc
New Table:
ID valueHeading1 valueHeading2
0 a a
1 ab NULL
2 abcd abc

If you need only join use this. Using case when is elegant way if you don't need join.
SELECT * FROM
(SELECT ID
,valueHeading
,value as 'valueHeading1'
FROM table1
WHERE valueHeading = 'valueHeading1') AS TAB_1,
(SELECT ID
,value as 'valueHeading2'
FROM table1
WHERE valueHeading = 'valueHeading2') AS TAB_2
WHERE TAB_1.ID = TAB_2.ID

Try something like :
SELECT ID
, CASE WHEN valueHeading = 'valueHeading1' THEN value ELSE NULL END AS valueHeading1
, CASE WHEN valueHeading = 'valueHeading2' THEN value ELSE NULL END AS valueHeading2
FROM table1
WHERE valueHeading IN ('valueHeading1', 'valueHeading2')
If you want to regroup all values on one row for each ID, you can try :
SELECT ID
, MAX(CASE WHEN valueHeading = 'valueHeading1' THEN value ELSE NULL END) AS valueHeading1
, MAX(CASE WHEN valueHeading = 'valueHeading2' THEN value ELSE NULL END) AS valueHeading2
FROM table1
WHERE valueHeading IN ('valueHeading1', 'valueHeading2')
GROUP BY ID
HAVING MAX(CASE WHEN valueHeading = 'valueHeading1' THEN value ELSE NULL END) IS NOT NULL
OR MAX(CASE WHEN valueHeading = 'valueHeading2' THEN value ELSE NULL END) IS NOT NULL
See SQLFiddle. I also tried on Oracle 11g and MSSQL 2012, and it works each time.

In SQLServer2005+ possible use PIVOT
SELECT ID, valueHeading1, valueHeading2
FROM
(
SELECT *
FROM dbo.test28
WHERE valueHeading IN ('valueHeading1', 'valueHeading2')
) x
PIVOT
(
MAX(value)
FOR valueHeading IN ([valueHeading1], [valueHeading2])
) p
Demo on SQLFiddle

self join could be a simple solution
SELECT DISTINCT t1.ID, t1.value as valueHeading1, t2.value as valueHeading2,
FROM table1 t1
INNER JOIN table1 t2 ON t1.ID = t2.ID
WHERE t1.valueHeading <> t2.valueHeading

Related

SQL CASE statement returns duplicate values

Here is how my data looks
title value
------------
t1 v1
t2 v2
t3 v3
Now I want t1 and t2 to be inferred as the same value t12. So, I do:
SELECT
CASE
WHEN title = 't1' OR title = 't2'
THEN 't12'
ELSE title
END AS inferred_title,
COUNT(value)
FROM
my_table
GROUP BY
inferred_title;
I expected the output to be:
inferred title values
-----------------------
t12 2
t3 1
But what I end up getting is:
inferred title values
--------------------------
t12 1
t12 1
t3 1
How do I make it behave the way I want it to? I don't want the duplicated rows.
The problem is scoping. You must have an inferred_title in the table. Either give a new column alias or repeat the expression:
SELECT (CASE WHEN title IN ('t1', 't2') THEN 't12'
ELSE title
END) AS inferred_title,
COUNT(value)
FROM my_table
GROUP BY (CASE WHEN title IN ('t1', 't2') THEN 't12'
ELSE title
END);
Do the "merge" case in a derived table (sub-query), group by its result:
SELECT inferred_title, COUNT(value)
FROM
(
SELECT CASE WHEN title = 't1' OR title = 't2' THEN 't12'
ELSE title
END AS inferred_title,
value
FROM my_table
) dt
GROUP BY inferred_title;
This saves you some typing, is less error prone and easier to maintain - and is
ANSI SQL compliant!
Select Title, COUNT(Title) AS Totals
From my_table
Group By Title
Having COUNT(Title)>1
Order By 2 desc

Get single row depending of conditional

I have a simple select query with some joins like:
SELECT
[c].[column1]
, [c].[column2]
FROM [Customer] AS [c]
INNER JOIN ...
So I do a left join with my principal table as:
LEFT JOIN [Communication] AS [com] ON [c].[CustomerGuid] = [com].[ComGuid]
this relatioship its 1 to *, one customer can have multiple communications
So in my select I want to get value 1 or 2 depending of condition:
Condition:
if ComTypeKey (from communication) table have a row with value 3 and have another row with vale 4 return 1 then 0
So I try something like:
SELECT
[c].[column1]
, [c].[column2]
, IIF([com].[ComTypeKey] = 3 AND [com].[ComTypeKey] = 4,1,0)
FROM [Customer] AS [c]
INNER JOIN ...
LEFT JOIN [Communication] AS [com] ON [c].[CustomerGuid] = [com].[ComGuid]
But it throws me two rows, beacause there are 2 rows on communication. My desire value is to get only one row with value 1 if my condition is true
If you have multiple rows you need GROUP BY, then count the relevant keys and subtract 1 to get (1, 0)
SELECT
[c].[column1]
, [c].[column2]
, COUNT(CASE WHEN [ComTypeKey] IN (3,4) THEN 1 END) - 1 as FLAG_CONDITION
FROM [Customer] AS [c]
INNER JOIN ...
LEFT JOIN [Communication] AS [com]
ON [c].[CustomerGuid] = [com].[ComGuid]
GROUP BY
[c].[column1]
, [c].[column2]
I'm not really sure I understand.
This will literally find if both values 3 and 4 exist for that CustomerGuid, and only select one of them in that case - not filtering out any record otherwise.
If this is not what you want, providing sample data with the expected result would remove the ambiguity.
SELECT Field1,
Field2,
...
FieldN
FROM (SELECT TMP.*,
CASE WHEN hasBothValues = 1 THEN
ROW_NUMBER() OVER ( PARTITION BY CustomerGuid ORDER BY 1 )
ELSE 1
END AS iterim_rn
FROM (SELECT TD.*,
MAX(CASE WHEN Value1 = '3' THEN 1 ELSE 0 END) OVER
( PARTITION BY CustomerGuid ) *
MAX(CASE WHEN Value1 = '4' THEN 1 ELSE 0 END) OVER
( PARTITION BY CustomerGuid ) AS hasBothValues
FROM TEST_DATA TD
) TMP
) TMP2
WHERE interim_rn = 1

Moving value from below row to upper one

How I can move value of a column to upper row where banakaccount is null
Below is my table data of two creditor TABLE1
UniqueDatabaseNo Creditor BankAccountNo
882370 300020 NULL
NULL 300020 NULL
NULL 300020 NULL
0 300020 NL21SOGE0946
NULL 300020 NULL
NULL 380910 NULL
0 380910 1432981
0 380910 NL98RABO0181
NULL 380910 NULL
2293483 380910 NULL
I NEED BELOW OUT PUT WHERE UniqueDatabaseNo > 0 AND ON SAME ROW BANK ACCOUNT SHOULD ON SAME ROW
Here is desired output
UniqueDatabaseNo Creditor BankAccountNo
882370 300020 NL21SOGE0946
2293483 380910 NL98RABO0181
I tried below query but it is not working correctly
select * from TABLE1
where uniquedatabaseno >0
union all
select * from TABLE1
where BankAccountNo LIKE '[a-Z][a-Z]%'
Thanks,
You can do what you want using aggregation:
select max(UniqueDatabaseNo) as UniqueDatabaseNo, Creditor,
max(case when BankAccountNo like '[a-Z][a-Z]%' then BankAccountNo end) as BankAccountNo
from t
group by Creditor;
Edit:
You might was conditional logic for UniqueDatabaseNo as well:
select max(case when UniqueDatabaseNo > 0 then UniqueDatabaseNo end) as UniqueDatabaseNo
This is not necessary for your sample data.
Try this
select UniqueDatabaseNo,Creditor,TT.BankAccountNo
from TABLE1 T1
OUTER APPLY(
SELECT BankAccountNo as 'BankAccountNo'
FROM TABLE1 T2
WHERE T1.Creditor=T2.Creditor AND T2.BankAccountNo IS NOT NULL
)TT
where T1.uniquedatabaseno >0 AND T1.UniqueDatabaseNo IS NOT NULL
You need to filter out NULL values:
SELECT * FROM TABLE1
WHERE
UniqueDatabaseNo > 0
AND BankAccountNo IS NOT NULL

Select statement to return constant when no records found in table in SQL Server 2008

I am have a table with data and now i need to return zero in select statement if there is no records in table for example. I need to use it in Stored Procedure.
-- If no records exists in below select statement
SELECT ID,Text,Date FROM tblData WHERE ID = 12
IF (##ROWCOUNT = 0)
BEGIN
SELECT -5 AS ID
END
Output:
ID Text Date
ID
-5
Expected output
ID
-5
If you want to return 1 row even when there is no match, you can use aggregation:
SELECT (CASE WHEN COUNT(*) = 0 THEN -5 ELSE MAX(ID) END) as ID
FROM tblData
WHERE ID = 12;
I always use an Exists statment.
if exists(SELECT ID FROM tblData WHERE ID = 12)
select 0 as RowsExist
else
select 1 as RowsExist
For a single scalar value you could use something like;
SELECT ISNULL((SELECT ID FROM tblData WHERE ID = 12), 0) as ID
Rhys
SELECT (CASE WHEN Ta.ID IS NULL THEN TBL.ID
ELSE Ta.ID END) AS ID,Ta.Text,Ta.Date
FROM (VALUES(-5)) AS TBL(ID)
LEFT JOIN
(
SELECT ID,Text,Date FROM tblData WHERE ID = 12
)
AS Ta ON Ta.ID = Ta.ID

Replace NULL with values

Here is my challenge:
I have a log table which every time a record is changed adds a new record but puts a NULL value for each non-changed value in each record. In other words only the changed value is set, the rest unchanged fields in each row simply has a NULL value.
Now I would like to replace each NULL value with the value above it that is NOT a NULL value like below:
Source table: Task_log
ID Owner Status Flag
1 Bob Registrar T
2 Sue NULL NULL
3 NULL NULL F
4 Frank Admission T
5 NULL NULL F
6 NULL NULL T
Desired output table: Task_log
ID Owner Status Flag
1 Bob Registrar T
2 Sue Registrar T
3 Sue Registrar F
4 Frank Admission T
5 Frank Admission F
6 Frank Admission T
How do I write a query which will generate the desired output table?
One the new windowed function of SQLServer 2012 is FIRST_VALUE, wich have quite a direct name, it can be partitioned through the OVER clause, before using it is necessary to divide every column in data block, a block for a column begin when a value is found.
With Block As (
Select ID
, Owner
, OBlockID = SUM(Case When Owner Is Null Then 0 Else 1 End)
OVER (ORDER BY ID)
, Status
, SBlockID = SUM(Case When Status Is Null Then 0 Else 1 End)
OVER (ORDER BY ID)
, Flag
, FBlockID = SUM(Case When Flag Is Null Then 0 Else 1 End)
OVER (ORDER BY ID)
From Task_log
)
Select ID
, Owner = FIRST_VALUE(Owner) OVER (PARTITION BY OBlockID ORDER BY ID)
, Status = FIRST_VALUE(Status) OVER (PARTITION BY SBlockID ORDER BY ID)
, Flag = FIRST_VALUE(Flag) OVER (PARTITION BY FBlockID ORDER BY ID)
FROM Block
SQLFiddle demo
The UPDATE query is easily derived
As I mentioned in my comment, I would try to fix the process that is creating the records rather than fixing the junk data. If that is not an option, the code below should get you pointed in the right direction.
UPDATE t1
set t1.owner = COALESCE(t1.owner, t2.owner),
t1.Status = COALESCE(t1.status, t2.status),
t1.Flag = COALESCE(t1.flag, t2.flag)
FROM Task_log as t1
INNER JOIN Task_log as t2
ON t1.id = (t1.id + 1)
where t1.owner is null
OR t1.status is null
OR t1.flag is null
I can think of several approaches.
You could use a combination of COALESCE with an array aggregate function. Unfortunately it doesn't look like SQL Server supports array_agg natively (although some nice people have developed some workarounds).
You could also use a subselect for each column.
SELECT id,
(SELECT TOP 1 FROM (SELECT owner FROM ... WHERE id = outer_id AND owner IS NOT NULL order by ID desc )) AS owner,
-- other columns
You could probably do something with window functions, too.
A vanilla solution would be:
select id
, owner
, coalesce(owner, ( select owner from t t2
where id = (select max(id) from t t3
where id < t1.id and owner is not null))
) as new_owner
, flag
, coalesce(flag, ( select flag from t t2
where id = (select max(id) from t t3
where id < t1.id and flag is not null))
) as new_flag
from t t1
Rather inefficient, but should work on most DBMS