SQL query grouped parameter maximum - sql

Let's say I had two columns in a database, col1 and col2. Column 2 is the time, Column 1 something. In my query, I want to do the following:
I want to SELECT * from my table and group the results by col1. However, I only want those entries where for the grouped col1 there is no value of col2 higher than a certain value. Meaning that, I only want those col1-s for which col2 does not exceed a certain value.
If, for instance, I had three rows, as follows:
ROW1: col1 = val1, col2 = 3
ROW2: col1 = val1, col2 = 5
ROW3: col1 = val2, col2 = 3
ROW4: col1 = val2, col2 = 4
And I do not want the time for any of them to exceed 4, then, as a result, I would only want ROW3 or ROW4, which, does not matter, for col1 is the same and is grouped. But in rows 1 and 2, that are grouped by col1's value "val1", in one of them col2 DOES exceed 4, therefore, I do not want any of them.

SELECT col1 FROM table GROUP BY col1 HAVING MAX(col2) <= 4
Because you want only the common value (col1) from the group, you can use GROUP BY. When you do a GROUP BY (aggregate) query, you can use the HAVING clause to apply a filter to the aggregated data set.

I am not use I got the point (my english is not good).
I think sub-query is the best choice.
Note: this example should work with mySql ...
SELECT *
FROM table
WHERE col1 IN
(SELECT col1 FROM table WHERE col2 < 5 GROUP BY col1)
ORDER BY col1

CREATE TABLE x (
t TIME NOT NULL,
v INT NOT NULL );
INSERT INTO x VALUES
('13:14:00', 24),
('13:14:00', 27),
('13:14:00', 29),
('17:12:00', 14),
('17:12:00', 20),
('17:12:00', 24);
SELECT t, MAX(v) AS mv FROM x
GROUP BY t
HAVING mv <= 25;
Or do I misunderstand the question?

Related

How can I return a list of row names and column names where the value is greater than 0 in SQL?

I've put together a reconciliation tool in SQL Server which identifies the number of record breaks by field (col 2 - col 4) between two identical (data types/structure) sources. The output returned is in the format below, grouped on col 1.
Col1 Col2 Col3 Col4
X 0 0 1
Y 0 1 1
Z 1 0 1
I am trying to manipulate the output so that it provides a list of the Col 1 identifier and the name of any column names (col 2 - col 4) which have breaks (value > 0).
The expected output based on the above data would look like this.
Col1 FieldBreak
X Col2
Y Col3
Y Col4
Z Col2
Z Col4
I'm newer to SQL (6 months of professional experience) and am stuck. Any help would be much appreciated!
In any database, you can use:
select col1, 'col2' as col
from t
where col2 = 1
union all
select col1, 'col3' as col
from t
where col3 = 1
union all
select col1, 'col4' as col
from t
where col4 = 1;
There are probably more efficient methods, but those depend on the database. And for a small table efficiency may not be a concern.
In SQL Server, you would unpivot using apply:
select t.col1, v.*
from t cross apply
(values ('col2', t.col2), ('col3', t.col3) . . .
) v(col, val)
where v.val is not null;
If you have a lot of columns, you can construct the expression using a SQL statement (from INFORMATION_SCHEMA.COLUMNS) and/or using a spreadsheet.

Count number of entries where Col1 has multiple entries with different data in Col2

I have a table with two Columns, Col1 and Col2. I want to retrieve only the data when the Col1 has multiple entries with different data in Col2.
For example, I want to retain:
Col1 Col2 Col1 Col2
AB-123456 AP-321654 AB-123456 AP-321654
AB-123456 AP-321789 AB-123456 AP-321789
AB-123456 AC-321456 AB-123456 AC-321456
AB-951357 AP-989898 AB-456851 AP-110211
AB-753159 AC-956854 AB-456851 AP-110279
AB-456851 AP-110211
AB-456851 AP-110279
I created through the Report feature a report that groups by Col1 and creates a subcount on the number of entries in Col2. Using the report, I seem to be having a problem with using the subtotal counter. If the subtotal counter is > 1, I want to report, otherwise skip and go to the next Col1 data.
My next option was to write VBA code to read through the table and output the multiples to a new data, then I could run that data through the report to format, etc. To count the multiples and use criteria, I thought perhaps the DCount function would work. I have tried different variations, but to no avail.
Ex:
NUM_OF_MULTS = DCount("Col1", "TBL_Of_Col1_Col2", Current_Col2 = Prev_Col2)
For Index1 = 1 to NUM_OF_MULTS ......
I tried different criteria, but it is either all the records or none.
I think the following should perform as you require:
select t1.* from YourTable t1
where exists (select 1 from YourTable t2 where t1.col1 = t2.col1 and t1.col2 <> t2.col2)
Change both occurrences of YourTable to suit your table name.
For every record, this uses a correlated subquery to test whether another record exists in the dataset with the same col1 value as the current record, but a different col2 value, thus meeting your criteria.
The use of select 1 is purely for optimisation: we don't care what data is returned by the subquery, only that it has records.
If you want to do it with SQL code you can use EXISTS:
select t.* from tablename as t
where exists (
select 1 from tablename
where Col1 = t.Col1 and Col2 <> t.Col2
)
and another option with the IN operator:
select * from tablename
where Col1 in (
select Col1
from (select distinct Col1, Col2 from tablename)
group by Col1
having count(*) > 1
)

SQL: Select the minimum value from multiple columns with null values

I have a table like this one
ID Col1 Col2 Col3
-- ---- ---- ----
1 7 NULL 12
2 2 46 NULL
3 NULL NULL NULL
4 245 1 792
I wanted a query that yields the following result
ID Col1 Col2 Col3 MIN
-- ---- ---- ---- ---
1 7 NULL 12 7
2 2 46 NULL 2
3 NULL NULL NULL NULL
4 245 1 792 1
I mean, I wanted a column containing the minimum values out of Col1, Col2, and Col 3 for each row ignoring NULL values. In a previous question (What's the best way to select the minimum value from multiple columns?) there is an answer for non NULL values. I need a query as efficient as possible for a huge table.
Select Id,
Case When Col1 < Col2 And Col1 < Col3 Then Col1
When Col2 < Col1 And Col2 < Col3 Then Col2
Else Col3
End As MIN
From YourTableNameHere
Assuming you can define some "max" value (I'll use 9999 here) that your real values will never exceed:
Select Id,
Case When Col1 < COALESCE(Col2, 9999)
And Col1 < COALESCE(Col3, 9999) Then Col1
When Col2 < COALESCE(Col1, 9999)
And Col2 < COALESCE(Col3, 9999) Then Col2
Else Col3
End As MIN
From YourTableNameHere;
You didn't specify which version of Teradata you're using. If you're using version 14+ then you can use least.
Unfortunately least will return null if any of its arguments are null. From the docs:
LEAST supports 1-10 numeric values.
If numeric_value is the data type of the first argument, the return
data type is numeric. The remaining arguments in the input list must
be the same or compatible types. If either input parameter is NULL,
NULL is returned.
But you can get around that by using coalesce as Joe did in his answer.
select id,
least(coalesce(col1,9999),coalesce(col2,9999),coalesce(col3,9999))
from mytable
This might work:
Select id, Col1, Col2, Col3, least(Col1, Col2, Col3) as MIN From YourTableNameHere
in this way you don't need to check for nulls, just use min and a subquery
select tbl.id,tbl.col1,tbl.col2,tbl.col3,
(select min(t.col)
from (
select col1 as col from tbl_name t where t.id=tbl.id
union all
select col2 as col from tbl_name t where t.id=tbl.id
union all
select col3 as col from tbl_name t where t.id=tbl.id
)t)
from tbl_name tbl
Output:
1 7 NULL 12 7
2 2 46 NULL 2
3 NULL NULL NULL NULL
4 245 1 792 1
Just modify your query with coalesce():
Select Id,
(Case When Col1 <= coalesce(Col2, col3, col1) And
Col1 <= coalesce(Col3, col2, col1)
Then Col1
When Col2 <= coalesce(Col1, col3, col2) And
Col2 <= coalesce(Col3, col1, col2)
Then Col2
Else Col3
End) As MIN
From YourTableNameHere;
This doesn't require inventing a "magic" number or over-complicating the logic.
I found this solution to be more efficient than using multiple case statement clauses, which can get extremely lengthy when evaluating data from several columns across one row.
Also, I can't take credit for this solution as I found it on some website a year or so ago. Today I needed a refresh on this logic, and I couldn't find it anywhere. I found my old code and decided to share it in this forum now.
Creating your test table:
create table #testTable(ID int, Col1 int, Col2 int, Col3 int)
Insert into #testTable values(1,7,null,12)
Insert into #testTable values(2,2,46,null)
Insert into #testTable values(3,null,null,null)
Insert into #testTable values(4,245,1,792)
Finding min value in row data:
Select ID, Col1, Col2, Col3 ,(SELECT Min(v) FROM ( VALUES (Col1), (Col2), (Col3) ) AS value(v)) [MIN] from #testTable order by ID

SQL query to find greatest in columns and rows

How does one in oracle find the greatest of three columns in say 10 rows?
The requirement is I have three dates column and I have need to find greatest of three columns in 10 rows. I know greatest will find in one row.
How?
How about
select max(greatest(date1, date2, date3, date4)) from my_table;
you can use greatest again in your order by
select * from (
select greatest(c1,c2,c3,c4) from mytable
order by greatest(c1,c2,c3,c4) desc
) t1 where rownum = 1
With data as(
Select col1 dt from table union all
Select col2 from table union all
Select col3 from table union all
Select col4 from table
)
Select max(dt) max_dt
from data
/
Assuming the 4 columns are DATE data type.
It only uses MAX, and not GREATEST.
Update : Expanding a good point mentioned by #Thorsten in below comment
The issue with GREATEST() function is that, whenever you need to handle the NULL values being passed to it, you must use NVL to get proper output. There have had been numerous questions on the subject, like "How to avoid NULL values with GREATEST function" etc.
Note : From performance point of view, please test it before applying such logic in your production environment.
Here are two ways to circumvent GREATEST's NULL problem (i.e. the problem that GREATEST not only returns NULL when all values are NULL, but already when at least one value is null, which makes working with GREATEST often a nuisance).
For n columns you can use n COALESCE expressions. Each expression must contain all columns, each beginning with a different column.
select
max(
greatest(
coalesce(col1,col2,col3,col4),
coalesce(col2,col3,col4,col1),
coalesce(col3,col4,col1,col2),
coalesce(col4,col1,col2,col3)
)
)
from mytable;
An alternative is not to use GREATEST at all, but compare with CASE and COALESCE. Each value gets compared with all other values. col1 >= coalesce(col2,col1) ensures that col1 is regarded greater or equal the expresson, when col2 is NULL, as long as col1 itself is not NULL. In CASE all columns are NULL the CASE expression defaults to NULL. (One could add else NULL to make this visible to the unexperienced reader.)
select
max(
case
when col1 >= coalesce(col2,col1) and col1 >= coalesce(col3,col1) and col1 >= coalesce(col4,col1) then col1
when col2 >= coalesce(col1,col2) and col1 >= coalesce(col3,col2) and col1 >= coalesce(col4,col2) then col2
when col3 >= coalesce(col1,col3) and col1 >= coalesce(col2,col3) and col1 >= coalesce(col4,col3) then col3
when col4 >= coalesce(col1,col4) and col1 >= coalesce(col2,col4) and col1 >= coalesce(col3,col4) then col4
end
)
from mytable;

Is there a SQL Statement that allows me to copy and insert existing rows but with one column change?

You might not understood what I want to ask from the title but ,here is the explanation.
I have a data in Oracle database table. What I wanted to do is insert a new data to the table. This new data is based on the existing data but I have to change the value of one columns. So if I have 10 rows in the database after the insertion i will have 20 rows but the new 10 rows contain the same data except on of the columns is changed.
E.g table before insertion a new data
Col1 Col2 Col3
a b AA
1 2 33
table after insertion a new data
Col1 Col2 Col3
a b **BB**
1 2 **44**
Provided that you can encode what the new value should be; yes.
INSERT INTO
myTable (
Col1,
Col2,
Col3
)
SELECT
Col1,
Col2, -- This is a specific example based on your comment.
Col3 + 6 -- This just adds 6 to the existing value, but any SQL
FROM -- could actually go here, such as a CASE statement...
myTable
So, the question becomes; Do you have rules that you can implement in SQL for calculating the new value for Col3?
The rules could be something basic like...
CASE WHEN Col3 = 'AA' THEN '**BB**'
WHEN Col3 = '33' THEN '**44**'
ELSE 'Unknown'
END,
Or you could have all the new values in another table and look them up using a join...
INSERT INTO
myTable (
Col1,
Col2,
Col3
)
SELECT
OldTable.Col1,
OldTable.Col2,
COALESCE(NewTable.Col3, 'Unknown')
FROM
myTable AS OldTable
LEFT JOIN
lookup AS NewTable
ON OldTable.Col1 = NewTable.Col1
AND OldTable.Col2 = NewTable.Col2
Or a whole bunch of other options.
It will depend on how you determine how to change the data. How do you know, for example, that AA should become BB or that 33 should become 44?
Something like this will work for the two cases you posted. You can adapt it to whatever rule you want by changing the CASE statement to compute the new value differently.
INSERT INTO table_name( col1, col2, col3 )
SELECT col1,
col2,
(CASE WHEN col3 = 'AA'
THEN 'BB'
WHEN col3 = '33'
THEN '44'
ELSE null
END)
FROM table_name;