Different Group By Clause on same Query (Select/Where) - sql

Can i reuse a complex select Where clause to different group by?
What i am doing:
(Select X, Count(*) From [Big where clause with many tables] Group By X)
Union All
(Select Y, Count(*) From [Big where clause with many tables] Group By Y)
How can i optmise this?
I'm using SQL Server 2008 and this will go inside a Function, but could be a stored procedure.

WITH basequery AS (Select * From [Big where clause with many tables])
SELECT X, COUNT(*) FROM basequery GROUP BY X
UNION ALL
SELECT Y, COUNT(*) FROM basequery GROUP BY Y;
CTEs were made for situations like this.

One option you could evaluate (particularly if there are relatively few x,y groups) would be to materialise the intermediate results yourself into a #table or #temp table then just SELECT from that (a #temp table would be better as you could set up better indexes than the below and it allows the select query that populates it to be parallelised but these are not available in a function)
DECLARE #T TABLE
(
X int,
Y int,
Cnt int,
UNIQUE(X,Y,Cnt), /*Cnt just included to make index covering*/
UNIQUE(Y,X,Cnt)
)
INSERT INTO #T
Select X, Y, Count(*)
From [Big where clause with many tables]
Group By X, Y
SELECT X, SUM(Cnt)
FROM #T
GROUP BY X
UNION ALL
SELECT Y, SUM(Cnt)
FROM #T
GROUP BY Y

SQL Server 2008 has introduced GROUPING SETS () which seems to be just what you are after. The UNION solution suggested by others can now easily be replaced with a single select with GROUPING SETS.
Basically, you are using it like this:
SELECT A, B, C
FROM …
WHERE …
GROUP BY GROUPING SETS ( (A), (B), (C) )
which is equivalent to
SELECT A, NULL, NULL, …
FROM …
WHERE …
GROUP BY A
UNION ALL
SELECT NULL, B, NULL, …
FROM …
WHERE …
GROUP BY B
UNION ALL
SELECT NULL, NULL, C, …
FROM …
WHERE …
GROUP BY C
So, your query might look like this:
SELECT X, Y, COUNT(*)
FROM your complex joins and filters
GROUP BY GROUPING SETS ( (X), (Y) )
Or like this:
SELECT
CASE WHEN X IS NULL THEN 'Y' ELSE 'X' END AS ObjType
CASE WHEN X IS NULL THEN Y ELSE X END AS Obj,
COUNT(*)
FROM your complex joins and filters
GROUP BY GROUPING SETS ( (X), (Y) )
The second one assumes that X cannot be NULL.
References:
GROUP BY (Transact-SQL)
GROUPING SETS Equivalents
Using GROUP BY with ROLLUP, CUBE, and GROUPING SETS

create view
CREATE VIEW SomeView
AS
Select X, Y, Count(*) AS C From [Big where clause with many tables]
then use the view:
Select X, C FROM SomeView GROUP BY X

Related

Is there a way to return a table given a input in sqlite?

I want to return the nth_value of a table given the value of an existing table column. But using that nested select affects the query's legibility: (ignore what demo is, just imagine that is a table with a sequential integer column ID)
WITH R (a,b) AS
(
VALUES(0, (SELECT first_value(ID) over() FROM demo))
UNION ALL
SELECT a+1 as a, (SELECT nth_value(ID, a+1) over() FROM demo) FROM R WHERE a<10
)
SELECT * FROM R
This would be better if I could make some kind of function that maps a given input to a nth_value of demo. Something that could make this could be like:
WITH R (a,b) AS
(
VALUES(0, get_demo_nth_value(0) )
UNION ALL
SELECT a+1 as a, get_demo_nth_value(a+1) FROM R WHERE a<10
)
SELECT * FROM R
We cannot create user-defined functions in SQLite, but is there a workaround? Something like a function that doesn't need to be defined as a procedure or other thing to map a value to a table.

Multiple Selects with Union disordered?

I have multiple selects joined by a Union as following
(
select * from x
)
UNION
(
select * from y
)
UNION
(
select * from z
)
But I'm getting disordered results on the SSMS:
z results
x results
y results
Is it possible to force results to be x, y, z?
You must provide an ORDER BY clause, otherwise no ordering is guaranteed. For example, the rows from different tables could even be interspersed.
A good option for explicit ordering, is to add a column and order by it
select *, 1 as ordering
from x
UNION
select *, 2
from y
UNION
select *, 3
from z
order by ordering;
I suspect you actually don't need UNION here (which implies DISTNCT) and you really want UNION ALL.

SQL Server query for all columns with group by and having

I'm wondering is there a way to query all columns with group by and having in SQL Server? For example, I have 6 columns, a, b,…,f, and this is something I want to get:
Select *
From table
Group by table.b, table.c
Having max(table.d)=table.d
This works in sybase, since I'm trying to migrate stuff from sybase to SQL Server, I'm not sure what I can do in new environment. Thanks.
Why do you want to group by every column when you don't use any aggragate-functions in your select? Just use the following code to get all columns of the table:
select * from table
Group by only gets used when you have aggragete-functions (e.g. max(), avg(), count(), ...) in your select.
Having limits the aggrageted columns and where the normal columns of the table.
You can use MIN, MAX, AVG, and COUNT functions with the OVER clause to provide aggregated values for each column (to imitate the group by clause for each column) and Common table expression CTE to filter out the results (to imitate the having clause) as:
;With CTE as
(
SELECT
MIN(a) OVER (PARTITION BY a) AS MinCol_a
, MAX(b) OVER (PARTITION BY b) AS MaxCol_b
, AVG(c) OVER (PARTITION BY c) AS AvgCol_c
, COUNT(e) OVER (PARTITION BY d) AS Counte_PerCol_d
FROM Tbl_Test
)
select MinCol_a,MaxCol_b ,AvgCol_c,Counte_PerCol_d
from CTE
Join --here you can join the table Test results with other tables
where --any filter condition similar to Having clause
If what you want is to get the rows with maximum d for each combination of b and c then use NOT EXISTS:
select t.* from tablename t
where not exists (
select 1 from tablename
where b = t.b and c = t.c and d > t.d
)
or with rank() window function:
select t.a, t.b, t.c, t.d, t.e, t.f
from (
select *,
rank() over (partition by b, c order by d desc) rn
from tablename
) t
where t.rn = 1
Without using having you can get the result which you want. Try below
Select table.b, table.c, max(table.d)
From table
Group by table.b, table.c

Select from a subset of data

I am working on a query in MS SQL Server 2014
That basically is a lot of unions
select x, y
where a = b
union
select x, y
where a = b
union
select x, y
where a = b
It works fine, however the where clauses are identical on every select. And for ease of maintenance I was wondering if there is a better, cleaner way to do this.
I was thinking of somehow selecting the data with the where clauses first then doing all the other queries only on this data.
But im open to any ideas on how to improve this query.
;WITH Test AS
(
SELECT x, y
UNION
SELECT x, y
UNION
SELECT x, y
)
SELECT * FROM Test
WHERE a = b
You could use a sub query and take the where clause outside of it for ease of maintenance.
Just make sure you bring all the columns through in the sub query that you will need in the where clause. Eg
SELECT * FROM
(
SELECT x,y,a,b FROM table1
union
SELECT x,y,a,b FROM table2
UNION
SELECT x,y,a,b FROM table3
)subquery
WHERE a=b
Select *
From
(
select x, y
union
select x, y
union
select x, y
) MyDerivedTable
Where ...
Make sure to include the columns you need to filter in the select statement of the tables inside the derived table.

SELECT on two other queries in Oracle

So, lets say I want to do something like:
SELECT Query1.a,
Query2.b
FROM (
SELECT q as a
FROM somewhere
),
(
SELECT g as b
FROM elsewhere
)
where Query 1 is
(
SELECT q as a
FROM somewhere
)
and Query2 is
(
SELECT g as b
FROM elsewhere
)
So, i want to select from two other select statements.
Query 1 produces a table
a
value1
Query 2 produces a table
b
value 2
And Query 3 (the outer select statement) produces
a b
value 1 value 2
So, essentially, two result tables are combined as columns and not as rows.
Thank you, if you have any hints.
You basically have your solution. You are only missing the names of your queries, so do like this:
SELECT Query1.a,
Query2.b
FROM (
SELECT q as a
FROM somewhere
) Query1,
(
SELECT g as b
FROM elsewhere
) Query2
It's not clear how you need to connect different rows from tables but it can be something like this:
select query1.a,
query2.b
FROM
(select q as a, ROW_NUMBER() OVER (ORDER BY q) as RN from a) Query1
FULL JOIN
(select q as b, ROW_NUMBER() OVER (ORDER BY q) as RN from b) Query2
ON Query1.RN=Query2.RN
SQLFiddle example
Your syntax is a bit off the SQL charts, but in essence ritgh:
It is possible to do a subquery:
select A.field from (select field from a_table) A;
It is essential that you name your query, if you want to use it in the select or where clauses.
And even possible to combine them like regular tables:
select A.field, B.other_field from (select field from table1) A, (select other_field from table2) B;
It is also possible to do al kind of where, grouping and sorting stuff on it, but not needed in your case.
I assume this is what you're looking for:
SELECT query1.a, query2.b
FROM
(SELECT q as a FROM somewhere) query1,
(SELECT g as b FROM elsewhere) query2
Here is a SQLFiddle to test the query