Select from union of nested subqueries - sql-server

I am quite certain this is an issue with applying proper aliases, I'm just not sure where I'm going wrong. I am looking at the following UNION in sqlserver:
Select Z.DesiredResult1, etc...
from (
Select C.columns
from (
Select B.columns
from (
Select A.columns
from (Subquery) as A
) as B
) as C
Where C.condition = 1
UNION
Select F.columns
from (
Select E.columns
from (
Select D.columns
from (Subquery) as D
) as E
) as F
Where F.condition = 2
) as Z
The union by itself functions perfectly, but when trying to make SELECT statements from it (as shown above) it throws an error:
No column name was specified for column 1 of 'Z'
Any insights would be appreciated, thanks for helping an SQL newbie.
Edit: Solved--I misunderstood the error. The issue was an aggregate function that needed an alias, not an entire subquery. Leaving the aggregate column unnamed worked fine for the union alone, so I didn't even consider it. Thanks for bothering to read.

This error can be easily reproduced. Check it here.
If you do not name the columns in a single UNION
SELECT *
FROM (SELECT 'A','B') T1
UNION
SELECT *
FROM (SELECT 'C','D') T2
You will get the same error:
No column name was specified for column 1 of 'T1'.
No column name was specified for column 2 of 'T1'.
No column name was specified for column 1 of 'T2'.
No column name was specified for column 2 of 'T2'.
Simply name each common column with the same name.
SELECT T3.Result1, T3.Result2
FROM
(SELECT *
FROM (SELECT 'A' Result1, 'B' Result2) T1
UNION
SELECT *
FROM (SELECT 'C' Result1, 'D' Result2) T2) T3
+----+---------+---------+
| | Result1 | Result2 |
+----+---------+---------+
| 1 | A | B |
+----+---------+---------+
| 2 | C | D |
+----+---------+---------+

Related

SQL GROUP BY with columns which contain mirrored values

Sorry for the bad title. I couldn't think of a better way to describe my issue.
I have the following table:
Category | A | B
A | 1 | 2
A | 2 | 1
B | 3 | 4
B | 4 | 3
I would like to group the data by Category, return only 1 line per category, but provide both values of columns A and B.
So the result should look like this:
category | resultA | resultB
A | 1 | 2
B | 4 | 3
How can this be achieved?
I tried this statement:
SELECT category, a, b
FROM table
GROUP BY category
but obviously, I get the following errors:
Column 'a' is invalid in the select list because it is not contained
in either an aggregate function or the GROUP BY clause.
Column 'b' is invalid in the select list because it is not contained in either an
aggregate function or the GROUP BY clause.
How can I achieve the desired result?
Try this:
SELECT category, MIN(a) AS resultA, MAX(a) AS resultB
FROM table
GROUP BY category
If the values are mirrored then you can get both values using MIN, MAX applied on a single column like a.
Seams you don't really want to aggregate per category, but rather remove duplicate rows from your result (or rather rows that you consider duplicates).
You consider a pair (x,y) equal to the pair (y,x). To find duplicates, you can put the lower value in the first place and the greater in the second and then apply DISTINCT on the rows:
select distinct
category,
case when a < b then a else b end as attr1,
case when a < b then b else a end as attr2
from mytable;
Considering you want a random record from duplicates for each category.
Here is one trick using table valued constructor and Row_Number window function
;with cte as
(
SELECT *,
(SELECT Min(min_val) FROM (VALUES (a),(b))tc(min_val)) min_val,
(SELECT Max(max_val) FROM (VALUES (a),(b))tc(max_val)) max_val
FROM (VALUES ('A',1,2),
('A',2,1),
('B',3,4),
('B',4,3)) tc(Category, A, B)
)
select Category,A,B from
(
Select Row_Number()Over(Partition by category,max_val,max_val order by (select NULL)) as Rn,*
From cte
) A
Where Rn = 1

Returning Field names as part of a SQL Query

I need to write a Sql Satement that gets passed any valid SQL subquery, and return the the resultset, WITH HEADERS.
Somehow i need to interrogate the resultset, get the fieldnames and return them as part of a "Union" with the origional data, then pass the result onwards for exporting.
Below my attempt: I have a Sub-Query Callled "A", wich returns a dataset and i need to query it for its fieldnames. ?ordinally maybe?
select A.fields[0].name, A.fields[1].name, A.fields[2].name from
(
Select 'xxx1' as [Complaint Mechanism] , 'xxx2' as [Actual Achievements]
union ALL
Select 'xxx3' as [Complaint Mechanism] , 'xxx4' as [Actual Achievements]
union ALL
Select 'xxx5' as [Complaint Mechanism] , 'xxx6' as [Actual Achievements] ) as A
Any pointers would be appreciated (maybe i am just missing the obvious...)
The Resultset should look like the table below:
F1 F2
--------------------- ---------------------
[Complaint Mechanism] [Actual Achievements]
xxx1 xxx2
xxx3 xxx4
xxx5 xxx6
If you have a static number of columns, you can put your data into a temp table and then query tempdb.sys.columns to get the column names, which you can then union on top of your data. If you will have a dynamic number of columns, you will need to use dynamic SQL to build your pivot statement but I'll leave that up to you to figure out.
The one caveat here is that all data under your column names will need to be converted to strings:
select 1 a, 2 b
into #a;
select [1] as FirstColumn
,[2] as SecondColumn
from (
select column_id
,name
from tempdb.sys.columns
where object_id = object_id('tempdb..#a')
) d
pivot (max(name)
for column_id in([1],[2])
) pvt
union all
select cast(a as nvarchar(100))
,cast(b as nvarchar(100))
from #a;
Query Results:
| FirstColumn | SecondColumn |
|-------------|--------------|
| a | b |
| 1 | 2 |

select resultset of counts by array param in postgres

I've been searching for this and it seems like it should be something simple, but apparently not so much. I want to return a resultSet within PostgreSQL 9.4.x using an array parameter so:
| id | count |
--------------
| 1 | 22 |
--------------
| 2 | 14 |
--------------
| 14 | 3 |
where I'm submitting a parameter of {'1','2','14'}.
Using something (clearly not) like:
SELECT id, count(a.*)
FROM tablename a
WHERE a.id::int IN array('{1,2,14}'::int);
I want to test it first of course, and then write it as a storedProc (function) to make this simple.
Forget it, here is the answer:
SELECT a.id,
COUNT(a.id)
FROM tableName a
WHERE a.id IN
(SELECT b.id
FROM tableName b
WHERE b.id = ANY('{1,2,14}'::int[])
)
GROUP BY a.id;
You can simplify to:
SELECT id, count(*) AS ct
FROM tbl
WHERE id = ANY('{1,2,14}'::int[])
GROUP BY 1;
More:
Check if value exists in Postgres array
To include IDs from the input array that are not found I suggest unnest() followed by a LEFT JOIN:
SELECT id, count(t.id) AS ct
FROM unnest('{1,2,14}'::int[]) id
LEFT JOIN tbl t USING (id)
GROUP BY 1;
Related:
Preserve all elements of an array while (left) joining to a table
If there can be NULL values in the array parameter as well as in the id column (which would be an odd design), you'd need (slower!) NULL-safe comparison:
SELECT id, count(t.id) AS ct
FROM unnest('{1,2,14}'::int[]) id
LEFT JOIN tbl t ON t.id IS NOT DISTINCT FROM id.id
GROUP BY 1;

SQL Server: Duplicate columns in joined table, but distinct row info

So I have joined two tables to identify claims and their corresponding reversals if there are any.
The following is a simplified explanation as to what I have done: Join where MbrNo is the same in both tables, and where Amount=-Amount. So now I have an output table contians duplicate column names:
MbrNo | ClaimType | Amount | MbrNo | ClaimType | Amount
xyz | Medicine | R 300 | xyz | Reversal | - R300
I can not input this in a table as column names are not unique.
But I would like to
1. Format this table to look as follows
MbrNo | ClaimType | Amount
xyz | Medicine | R 300
xyz | Reversal | - R300
with t as
(
select *,
count(*) over(partition by [MbrNo], [DepNo], [PracticeNo], [DisciplineCd], [ServiceDt],[PayAmt]) as rownum
from Claims
)
Select * from
(Select * from t where PayAmt<0) a
left outer join
(Select * from t where PayAmt>0) b
on a.[MbrNo]=b.[MbrNo]
and a.[DepNo]=b.[DepNo]
and a.[PracticeNo]=b.[PracticeNo]
and a.[DisciplineCd]=b.[DisciplineCd]
and a.[ServiceDt]=b.[ServiceDt]
and a.[PayAmt]=-b.[PayAmt]
Basically I want to put the 2nd table in the joined table underneath the first table.
Please help:(
If I've understood your requirements correctly then I think you want the UNION operator. See if this gets you going in the right direction.
with t as
(
select *,
count(*) over(partition by [MbrNo], [DepNo], [PracticeNo], [DisciplineCd], [ServiceDt],[PayAmt]) as rownum
from Claims
)
Select t.* from t where PayAmt < 0
union all
select b.* from
(Select * from t where PayAmt < 0) a
inner join
(Select * from t where PayAmt > 0) b
on a.[MbrNo] = b.[MbrNo]
and a.[DepNo] = b.[DepNo]
and a.[PracticeNo] = b.[PracticeNo]
and a.[DisciplineCd] = b.[DisciplineCd]
and a.[ServiceDt] = b.[ServiceDt]
and a.[PayAmt] = -b.[PayAmt]

More efficient alternative to subquery

I've got many "actual" and "history companion tables" (so to speak) with structure of last like this:
values| date_deal | type_deal | num (autoinc)
value1| 01.01.2012 | i | 1
value1| 02.01.2012 | u | 2
value2| 02.01.2012 | i | 3
value2| 03.01.2012 | u | 4
value1| 04.01.2012 | d | 5
value2| 05.01.2012 | u | 6
value2| 08.01.2012 | u | 7
If I insert (or update or delete) record in "actual" table, trigger puts affected record into "history table" with date_deal = Geddate(), type_deal = i|u|d (for insert, update and delete triggers respectivly) and num as autoinc unique value
So the question is how to get last record for each distinct value valid on certain date and excluding from final result records which type_deal = 'd' (since that record was deleted from actual table by that time and we don't want to have anything assosiated with it)
The way I do it most of the time:
SELECT *
FROM t_table1 t1
WHERE t1.num = ( SELECT MAX(num)
FROM t_table1 t2
WHERE t2.[values] = t1.[values]
AND t2.[date_deal] < #dt)
AND t1.[type_deal] <> 'D'
But that works very slow sometimes. I'm looking for more efficient alternative. Please, help
So, an update.
Thanks for replies, friends.
I've made some testing on both actual and testing servers.
In order to put these different approaches into same league I've decided that we should take all fields from source table.
Testing server has bellow 200K records and I also had a luxury of using DBCC FreeProcCache
and DBCC DropCleanbuffers directives. Actual working server has over 2.3M records and also no option for droping buffs or cache since.. well.. it is in use by real users. So it was droped only once and i've got results right after that.
Here is actual queries and time it took on both servers:
Original:
DECLARE #dt datetime = CONVERT(datetime, '01.08.2013', 104)
SELECT *
FROM [CLIENTS_HISTORY].[dbo].[Clients_all_h] c
WHERE c.num = ( SELECT MAX(num)
FROM [CLIENTS_HISTORY].[dbo].[Clients_all_h] c2
WHERE c2.[AccountSys] = c.[AccountSys]
AND date_deal <= #dt)
AND c.type_deal <> 'D'
61sec # 2'316'890rec on real one, 4sec # 191'533 on test
Rahul's:
SELECT *
FROM [CLIENTS_HISTORY].[dbo].[Clients_all_h] c
GROUP BY [all_fields]
HAVING c.num = ( SELECT MAX(num)
FROM [CLIENTS_HISTORY].[dbo].[Clients_all_h] c2
WHERE c2.[AccountSys] = c.[AccountSys]
AND date_deal <= #dt)
AND c.type_deal <> 'D'
62sec # 2'316'890rec on real one, 4sec # 191'533 on test
Almost equal
George's (with some major changes):
SELECT * FROM
(
SELECT *,
ROW_NUMBER() OVER(PARTITION BY accountsys ORDER BY num desc) AS aa
FROM [CLIENTS_HISTORY].[dbo].[Clients_all_h] c
WHERE c.date_deal < #dt) as a
WHERE aa=1
AND type_deal <> 'D'
76sec # 2'316'890rec on real one, 5sec # 191'533 on test
So far original and Rahul's are fastest and George's is not so fast.
Try using GROUP BY..HAVING CLAUSE
SELECT *
FROM t_table1 t1
GROUP BY [column_names]
HAVING t1.num = ( SELECT MAX(num)
FROM t_table1 t2
WHERE t2.[values] = t1.[values]
AND t2.[date_deal] < #dt)
AND t1.[type_deal] <> 'D'
I think row_num() could be usefull for you as follows:
select
*
from
(
select
*,
row_number() over( partition by date_deal order by num) as aa
from
t_table1 t1
where
t1.[type_deal] <> 'D'
) as a
where
aa=1

Resources