SQL Server query for all columns with group by and having - sql

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

Related

Selecting random value for every row

Suppose I have 2 tables called 'FOR_TEST_1' with column A, B, C and 'FOR_TEST_2' with column D, E, F.
I would like to generate column A paired with a random value from column D.
Here is a snippet of the tables.
So far, this is the statement that I have been doing and it return the same value of D for every row in A.
Currently I am using toad for oracle, but I tried using the same logic in MySQL and it works fine.
You're expecting Oracle to execute the subquery once per row (which is what MySQL does). However, it seems you have run into a side-effect of an Oracle optimization. There's no correlation between the main query and the scalar subquery so Oracle decides to un-nest the subquery, execute it once and join the result to the main query.
To get the results you want you have a couple of options. One is to turn off the unnesting with the NO_UNNEST hint.
select t1.a
, ( select d from ( select /*+ NO_UNNEST */ d from for_test_two
order by dbms_random.value ) where rownum = 1) d
from for_test_one t1
/
Alternatively you could rewrite you query to use an inline view rather than a scalar subquery.
select t1.a
, t2.d
from ( select a, rownum as rn
from for_test_one) t1
join ( select d, rownum as rn
from ( select d from for_test_two
order by dbms_random.value() ) ) t2
on t1.rn = t2.rn
order by t1.rn
/
Warning: The NO_UNNEST solution doesn't work on SQL Fiddle demo (find it here). Not sure why, the syntax looks correct. So try it on your environment, or just use the second approach, which definitely works.
Try this:
SELECT A, (SELECT D (
SELECT D, ROWNUM ROWPTR FROM FOR_TEST_2)
WHERE ROWPTR = (SELECT ROUND(DBMS_RANDOM.VALUE(1, (SELECT COUNT(D) FROM FOR_TEST_2) + 1 )) from DUAL)) D
FROM FOR_TEST_1

Keep Track of already summed tuples sql

If we have a table with values for a and b, is there a way to only add up the b's if its not a duplicate a? For example
a b
1 2
2 3
2 3
so we would get only 5 (instead of 8)
A sort of
select sum(b if unique a),
from table
where ...
The following query selects the lowest value of b for each group a
select min(b) min_b
from mytable
group by a
You can then sum those values by selecting the sum from a derived table
select sum(min_b) from (
select min(b) min_b
from mytable
group by a
) t
http://sqlfiddle.com/#!9/d82c5/1
You haven't specified your RDBMS, but if you are using a database which supporting window functions like SQL Server, you can query the unique rows first by using WITH clause and ROW_NUMBER() function and then get the SUM out of that.
;WITH C AS(
SELECT a, b,
ROW_NUMBER() OVER (PARTITION BY a ORDER BY a) AS Rn
FROM Table1
)
SELECT SUM(b) FROM C
WHERE Rn = 1
SQL Fiddle

Aggregate two columns and rows into one

I have the following table structure
start|end
09:00|11:00
13:00|14:00
I know
SELECT ARRAY_AGG(start), ARRAY_AGG(end)
Will result in
start|end
[09:00,13:00]|[11:00,14:00]
But how can i get the following result?
result
[09:00,11:00,13:00,14:00]
BTW, I'm using Postgres
You could do array concatenation (if order is not important):
SELECT ARRAY_AGG(start) || ARRAY_AGG(end) FROM TABLE1
If order is important you could use Gordon's approach but:
add aggregate order array_agg(d order by d ASC)
use unnest instead of union all, because Gordon's solution (union all) performs two sequence scan. If table is big it could be better for performance to use:
SELECT array_agg(d ORDER BY d ASC) FROM(
SELECT unnest(ARRAY[start] || ARRAY[end]) as d from table1
) sub
which performs only one sequence scan on table (and will be faster).
One method is to unpivot them and then aggregate:
select array_agg(d)
from (select start as d from t
union all
select end as d from t
) t;
A similar method uses a cross join:
select array_agg(case when n.n = 1 then t.start else t.end end)
from t cross join
(select 1 as n union all select 2) n;
I assume the start and end are character type
select ARRAY_AGG(col)
from(select string_agg(strt::text||','||en::text,',') col
from b
)t

How to get Original Rows filtered by a HAVING Condition?

What is the method in T-SQL to select the orginal values limited by a HAVING attribute. For example, if I have
A|B
10|1
11|2
10|3
How would I get all the values of B (Not An Average or some other summary stat), Grouped by A, having a Count (Occurrences of A) greater than or equal two 2?
Actually, you have several options to choose from
1. You could make a subquery out of your original having statement and join it back to your table
SELECT *
FROM YourTable yt
INNER JOIN (
SELECT A
FROM YourTable
GROUP BY
A
HAVING COUNT(*) >= 2
) cnt ON cnt.A = yt.A
2. another equivalent solution would be to use a WITH clause
;WITH cnt AS (
SELECT A
FROM YourTable
GROUP BY
A
HAVING COUNT(*) >= 2
)
SELECT *
FROM YourTable yt
INNER JOIN cnt ON cnt.A = yt.A
3. or you could use an IN statement
SELECT *
FROM YourTable yt
WHERE A IN (SELECT A FROM YourTable GROUP BY A HAVING COUNT(*) >= 2)
A self join will work:
select B
from table
join(
select A
from table
group by 1
having count(1)>1
)s
using(A);
You can use window function (no joins, only one table scan):
select * from (
select *, cnt=count(*) over(partiton by A) from table
) as a
where cnt >= 2

Select and sums from another table. Whats wrong with this SQL?

Whats wrong with this SQL?
SELECT Id, (select SUM(VALUE) from SomeTable) AS SumValue, GETDATE()
FROM MyTable
WHERE SumValue > 0
You cannot use aliased columns in the SELECT clause in the same query, except in ORDER BY.
It needs to be subqueried
SELECT Id, SumValue, GETDATE()
FROM (
SELECT Id, (select SUM(VALUE) from TABLE) AS SumValue
FROM MyTable
) X
WHERE SumValue > 0
That is the general case. For your specific query, it doesn't make sense because the subquery is not correlated to the outer query, so either NO rows show, or ALL rows show (with the same SumValue). I will simply assume you have simplified the query a lot since a table name of "table" doesn't really work.
I would probably rewrite like this:
SELECT a.Id, b.SumValue, GETDATE() as [now]
FROM MyTable a
Join
(
select id, SUM(VALUE) as [SumValue]
from [TABLE]
Group by id
)b on a.Id = b.Id
WHERE b.SumValue > 0
This is assuming that the value you are totalling relates to the ID in your table?
right way is
SELECT Id, (select SUM(VALUE) from TABLE) AS SumValue, GETDATE()
FROM MyTable
WHERE (select SUM(VALUE) from TABLE) > 0