Oracle SQL foreach - sql

I have this table:
+------+------+------+------+
| User | Val1 | Val2 | Val3 |
+------+------+------+------+
| Usr1 | v4 | a | x |
+------+------+------+------+
| Usr2 | v4 | c | y |
+------+------+------+------+
| Usr3 | v6 | b | z |
+------+------+------+------+
| Usr4 | v5 | d | z |
+------+------+------+------+
| Usr5 | v4 | c | z |
+------+------+------+------+
The attributes of Val1 and Val2 aren't static (with the time it's possible to have Val1=v6, v7, etc. and Val2=f,g,h, etc.).
So, i need to obtain this result:
Name | Number
v4 | 3
a | 1
c | 2
v6 | 1
b | 1
v5 | 1
d | 1
Where Name is the value of Val1 and Val2, Number the count of their occurrences
If i'm in a functional program language i can use a foreach operator...
There’s any solution to do this in ONE query with SQL for oracle DB?
edit
in Pl\SQL, it's possible?

select "Val1" as "name", "Val2", count(0) as "number"
from your_table
group by "Val1", rollup("Val2")
order by "Val1", GROUPING("Val2") desc, "Val2"
fiddle

The way you do this in SQL is:
select val1,
(case when grouping(val2) = 1 then 'Total' else val2 end) as val2,
count(*) as "Number"
from t
group by rollup(val1, val2)
This doesn't do exactly what you want in terms of output. Remember though that SQL tables and result sets have well-defined columns. Final output for reporting purposes is often done in the application.

SQL> with t (col1, col2) as
2 (
3 select 'v4','a' from dual union all
4 select 'v4','c' from dual union all
5 select 'v6','b' from dual union all
6 select 'v5','d' from dual union all
7 select 'v4','c' from dual
8 )
9 select decode(grouping(col2),1,col1,col2) col, count(*)
10 from t
11 group by rollup(col1, col2)
12 having grouping(col1)*grouping(col2) = 0
13 order by col1, grouping(col2) desc, col2
14 /
CO COUNT(*)
-- ----------
v4 3
a 1
c 2
v5 1
d 1
v6 1
b 1

Related

MS SQL Server multiple column sort independently

So to get things out of the way, due to structure i'm left in a situation of trying to sort some columns of data in SQL in a way that is evading me.
The problem is that i need multiple sets of 2 columns sorted independently, For example i have something like this:
Name | Val1 | Name | Val2 | Name | Val3
A | 2 | A | 1 | A | 3
B | 1 | B | 3 | B | 2
C | 3 | C | 2 | C | 1
and i need the table to be sorted by the highest of each value:
Name | Val1 | Name | Val2 | Name | Val3
C | 3 | B | 3 | A | 3
A | 2 | C | 2 | B | 2
B | 1 | A | 1 | C | 1
I don't seem to know how to organise using ROW_NUMBER() and various other things i have done through long searches being able to separate out individual columns for ordering but i don't know how i can keep two linked while the others sort independently, Can anyone help?
EDIT:
The Data is Extrapolated from one table after calculations had been done for their values.
So say i have my table of:
Name | Val1 | Val2 | Val3 |
A | 2 | 1 | 3 |
B | 1 | 3 | 2 |
C | 3 | 2 | 1 |
The values are names specifically are just used for example, but are wildly Differing values.
So from that table of final results i need to get the results in the format that the name with the highest value will be on top for each individual value
SELECT Name AS N1,
Val1,
Name As N2,
Val2
etc
EDIT: Example:
Name1|Units|Name2|Units| Name3|Units
AF |218 |AF |0.83 | AF |1.04
AD |172 |AD |0.49 | AD |1.05
AF |116 |AF |0.87 | AF |1.06
AF |324 |AF |0.84 | AF |1.10
If I understand your question correctly, consider the following approach:
CREATE TABLE #NameValue (
Name varchar(10),
Val1 int,
Val2 int,
Val3 int
)
INSERT INTO #NameValue
VALUES
('A', 102, 201, 303),
('B', 101, 203, 302),
('C', 103, 202, 301);
WITH nv1 AS (
SELECT Name, Val1, ROW_NUMBER() OVER (ORDER BY Val1 DESC) AS RN1
FROM #NameValue
),
nv2 AS (
SELECT Name, Val2, ROW_NUMBER() OVER (ORDER BY Val2 DESC) AS RN2
FROM #NameValue
),
nv3 AS (
SELECT Name, Val3, ROW_NUMBER() OVER (ORDER BY Val3 DESC) AS RN3
FROM #NameValue
)
SELECT
nv1.Name AS Name1, nv1.Val1,
nv2.Name AS Name2, nv2.Val2,
nv3.Name AS Name3, nv3.Val3
FROM nv1
LEFT JOIN nv2 ON (nv1.RN1 = nv2.RN2)
LEFT JOIN nv3 ON (nv1.RN1 = nv3.RN3)
Output:
Name1 Val1 Name2 Val2 Name3 Val3
C 103 B 203 A 303
A 102 C 202 B 302
B 101 A 201 C 301

SQL select distinct when one column in and another column greater than

Consider the following dataset:
+---------------------+
| ID | NAME | VALUE |
+---------------------+
| 1 | a | 0.2 |
| 1 | b | 8 |
| 1 | c | 3.5 |
| 1 | d | 2.2 |
| 2 | b | 4 |
| 2 | c | 0.5 |
| 2 | d | 6 |
| 3 | a | 2 |
| 3 | b | 4 |
| 3 | c | 3.6 |
| 3 | d | 0.2 |
+---------------------+
I'm tying to develop a sql select statement that returns the top or distinct ID where NAME 'a' and 'b' both exist and both of the corresponding VALUE's are >= '1'. Thus, the desired output would be:
+---------------------+
| ID | NAME | VALUE |
+---------------------+
| 3 | a | 2 |
+----+-------+--------+
Appreciate any assistance anyone can provide.
You can try to use MIN window function and some condition to make it.
SELECT * FROM (
SELECT *,
MIN(CASE WHEN NAME = 'a' THEN [value] end) OVER(PARTITION BY ID) aVal,
MIN(CASE WHEN NAME = 'b' THEN [value] end) OVER(PARTITION BY ID) bVal
FROM T
) t1
WHERE aVal >1 and bVal >1 and aVal = [Value]
sqlfiddle
This seems like a group by and having query:
select id
from t
where name in ('a', 'b')
having count(*) = 2 and
min(value) >= 1;
No subqueries or joins are necessary.
The where clause filters the data to only look at the "a" and "b" records. The count(*) = 2 checks that both exist. If you can have duplicates, then use count(distinct name) = 2.
Then, you want the minimum value to be 1, so that is the final condition.
I am not sure why your desired results have the "a" row, but if you really want it, you can change the select to:
select id, 'a' as name,
max(case when name = 'a' then value end) as value
you can use in and sub-query
select top 1 * from t
where t.id in
(
select id from t
where name in ('a','b')
group by id
having sum(case when value>1 then 1 else 0)>=2
)
order by id

SQL Count across columns

I know that this table structure is horrible and that I should look into database normalization, but this is what I have to work with at the moment.
I need to find the most common number across the columns where one of them has a specific id (in my example 3). Both columns will never have the same value.
Query
SELECT Col1, Col2 FROM scores WHERE Col1 = 3 OR Col2 = 3
Result
+------+------+
| Col1 | Col2 |
+------+------+
| 1 | 3 |
| 3 | 1 |
| 2 | 3 |
| 6 | 3 |
| 3 | 7 |
| 3 | 9 |
| 2 | 3 |
| 5 | 3 |
+------+------+
I'm hoping to get a result like this (I don't need count for 3 since it's the ID, but it can be included)
+-------+-------+
| Value | Count |
+-------+-------+
| 1 | 2 |
| 2 | 2 |
| 5 | 1 |
| 6 | 1 |
| 7 | 1 |
| 9 | 1 |
+-------+-------+
I've tried a few things such as UNION and nested SELECT but that doesn't seem to solve this thing.
Any suggestions?
If you want a count of the values where the OTHER column is 3, then a UNION would work like this:
SELECT value, theCount = COUNT(*)
FROM (
SELECT value = col1
FROM scores
WHERE col2 = 3
UNION ALL
SELECT col2
FROM scores
WHERE col1 = 3) T
GROUP BY value
ORDER BY value;
One way is using case:
SELECT
case Col1 when 3 then Col2 else Col1 end,
count(*)
FROM scores
WHERE Col1 = 3 OR Col2 = 3
Group by
case Col1 when 3 then Col2 else Col1 end;

display records based on ranks and also delete duplicated data

i have a table like this
+------+------+------+------+
| col1 | col2 | col3 | rank |
+------+------+------+------+
| 1 | A | X | 4 |
| 2 | C | Y | 3 |
| 2 | C | Y | 3 |
| | A | X | 3 |
| 1 | B | Z | 2 |
+------+------+------+------+
(5 rows)
I need o/p like this
+------+------+------+------+
| col1 | col2 | col3 | rank |
+------+------+------+------+
| 1 | A | X | 4 |
| 2 | C | Y | 3 |
| 1 | B | Z | 2 |
+------+------+------+------+
so that I written query like below
select col1,col2,col3,rank,dense_rank() over(order by rank desc) from table1;
but its not giving proper o/p
try this !!
select a.col1,a.col2,a.col3,max(a.rank) as rank
from [dbo].[5] a join [dbo].[5] b
on a.col1=b.col1 group by a.col1,a.col2,a.col3
looks like you need aggregation with max():
select
col1,col2,col3,
max(rnk)
from table1
group by col1,col2,col3
If you could have different values of col1 for one combination of col2, col3, then distinct on is what you need:
select distinct on (col2, col3)
col1,col2,col3,
rnk
from table1
order by col2, col3, rnk desc
sql fiddle demo
The following should match what you are looking for:
select col1,col2,col3,rank,dense_rank() over(order by rank desc) from table1
WHERE col1 IS NOT NULL
GROUP BY 1, 2, 3, 4;
You can also use numeric aliases in your order by clause if you want one.

Find list of values in list of values

I'm trying to write a sql with a where clause, that checks if any element in a list is in another list. Is there a shorter way to accomplish this rather than check each member of the first list?
SELECT * from FOO
WHERE FOO.A IN ('2','3', '5', '7','11','13','17','19') OR
FOO.B IN ('2','3', '5', '7','11','13','17','19') OR
FOO.C IN ('2','3', '5', '7','11','13','17','19') OR
FOO.D IN ('2','3', '5', '7','11','13','17','19') OR
FOO.E IN ('2','3', '5', '7','11','13','17','19') OR
FOO.F IN ('2','3', '5', '7','11','13','17','19')
That is the simplified sql.
Was trying not to muddy waters too much, but since you ask:
Ultimately what I am trying to do here is, select rows from FOO, that has columns fulfilling various criteria. These criteria are stored in a second table (call it BAR), mainly db, name, type must match and flag must be 1. Was planning to build the IN list from BAR, comparing them with column names in INFORMATION_SCHEMA.COLUMNS containing FOO
FOO:
+--------+--------+---------+---------+--------+-------+
| DB | Name | Type | Col1 | Col2 | Col3 |
+--------+--------+---------+---------+--------+-------+
| 4 | AC1 | LO | 1 | 10 | 2 |
| 4 | AC1 | HI | 2 | 20 | 4 |
| 1 | DC2 | HI-HI | 11 | 5 | 2 |
| 1 | DC2 | HI | 22 | 10 | 4 |
| 1 | DC2 | LO | 33 | 15 | 6 |
+--------+--------+---------+---------+--------+-------+
BAR:
+--------+--------+---------+---------+--------+
| DB | Name | Type | Field | Flag |
+--------+--------+---------+---------+--------+
| 4 | AC1 | LO | Col1 | 1 |
| 4 | AC1 | HI | Col1 | 1 |
| 1 | DC2 | HI-HI | Col1 | 1 |
| 1 | DC2 | HI | Col1 | 1 |
| 1 | DC2 | LO | Col1 | 1 |
| 4 | AC1 | LO | Col2 | 0 |
| 4 | AC1 | HI | Col2 | 0 |
| 1 | DC2 | LO | Col2 | 0 |
| 1 | DC2 | HI-HI | Col2 | 0 |
| 1 | DC2 | HI | Col2 | 0 |
| 4 | AC1 | LO | Col3 | 0 |
| 4 | AC1 | HI | Col3 | 0 |
| 1 | DC2 | LO | Col3 | 0 |
| 1 | DC2 | HI-HI | Col3 | 0 |
| 1 | DC2 | HI | Col3 | 0 |
+--------+--------+---------+---------+--------+
On first examination, it would seem your schema is not appropriate for the type of query you're performing. It seems like you would want a FOOVAL table with a type and a value then you're query simply becomes:
CREATE TABLE FOOVAL
{
ID int, -- References FOO.ID
TYPE char, -- A, B, C, D, E, F
VAL int
}
SELECT * FROM FOO WHERE FOO.ID IN
(SELECT DISTINCT FOOVAL.ID WHERE FOOVAL.VAL IN ('2','3', '5', '7','11','13','17','19'))
Your method probably performs the best. Here is an alternative that only requires creating the list once. It uses a CTE to create a list of the values and then an exists clause to check whether any values match:
with vals as (
select '2' as p union all
select '3' union all
select '5' union all
select '7' union all
select '11' union all
select '13' union all
select '17' union all
select '19'
)
select *
from foo
where exists (select 1 from vals where vals.p in (foo.A, foo.B, foo.C, foo.D, foo.E, foo.F))
If you are using a database that doesn't support CTEs, you can just put the code in the where clause:
select 8
from foo
where exists (select 1
from (select '2' as p union all
select '3' union all
select '5' union all
select '7' union all
select '11' union all
select '13' union all
select '17' union all
select '19'
) t
where vals.p in (foo.A, foo.B, foo.C, foo.D, foo.E, foo.F)
)
If you are using Oracle, then you need to add from dual in the statements after the string constants. Otherwise, I think one or the other should work in any SQL database.
While it is not exactly clear what you want to do with the data, since you are using SQL Server my suggestion would be to use the UNPIVOT function to turn the col1, col2 and col3 columns into rows which will make it easier to filter the data:
select db, name, type, col, value
from foo
unpivot
(
value
for col in (Col1, Col2, Col3)
) unpiv;
See SQL Fiddle with Demo. This gives the data in the following format:
| DB | NAME | TYPE | COL | VALUE |
------------------------------------
| 4 | AC1 | LO | Col1 | 1 |
| 4 | AC1 | LO | Col2 | 10 |
| 4 | AC1 | LO | Col3 | 2 |
| 4 | AC1 | HI | Col1 | 2 |
Once the is in the row format, it should be significantly easier to apply any filters or even join to your BAR table.