SQL conditional select for value in the same table group - sql

We have a table like this:
id mid mult tr result
----------------------------
1 1 1,35 0 1
2 1 5,85 0 2
3 1 4 1 X
50 2 1,3 1 1
51 2 7 0 2
52 2 4 0 X
99 3 2,45 0 1
100 3 2,2 0 2
101 3 3,3 1 X
105 4 2,3 0 1
106 4 2,4 0 2
107 4 3,2 1 X
111 5 3 1 1
112 5 1,9 0 2
113 5 3,25 0 X
What we need is a query that will bring us a table which will count how many times before, for a specific mid(match), have the same mults(multipliers) for result = 1 and result = x have occurred and group them so that we can count them. Something like
select mult(where result = 1), mult(where result = x), count(1)
The result will be like this
mult_1 mult_x count
------------------------------
1,35 4 33
1,3 4 112
The above states that we have seen 33 matches where the mult for result = 1 was 1,35 AND mult for result = x was 4. Also, we found 112 matches where multi for result = 1 was 1,3 AND for result = x mult was 4 (results are not dependant on the first table).
I find it rather complex myself, but hopefully I made sense. I use SQL Server 2008, so any method is more than welcome.

Here is my (final) solution in action: http://www.sqlfiddle.com/#!3/3a516/7
There are a number of assumptions I'm making here.
I'm assuming that every mid value is going to have at most 1 result value of '1' and 1 result of '2'.
I'm assuming that if you want to specify a mid and have the query run off of that.
This assumption turned out to be wrong.
I'm assuming that you want to include the data for the mid you specify.
This assumption turned out to be wrong, it was every match.
I'm assuming that you want to include every mid (or match) before the mid you specify.
This assumption turned out to be wrong, it was every match.
I'm assuming that for any mult_1 and mult_x combination you find in this way, you want the count of how many times that combination occurs, even if it occurs after the match(mid) that you specified.
This assumption turned out to be wrong, it was just for every match.
If all of these assumptions sound right, then this might create the result you're looking for:
DECLARE #Mid INT = 2
;WITH MatchResults AS
(
SELECT
Mid
, [1] AS MultWithResult1
, [X] AS MultWithResultX
FROM
(
SELECT
Mid
, mult
, result
FROM Matches
WHERE result IN ('1', 'X')
) Base
PIVOT
(
MAX(mult)
FOR result
IN
(
[1]
, [X]
)
) Pivoted
)
SELECT
mult.MultWithResult1 AS mult_1
, mult.MultWithResultX AS mult_x
, COUNT(*) AS [count]
FROM MatchResults mult
GROUP BY mult.MultWithResult1
, mult.MultWithResultX
EDIT: I have edited it based on the response to my answer to what I think he means.

Use this query:
select mult_1, mult_x, count() as count
from Matches M
inner join (select distinct M1.mult as mult_1, MX.mult as mult_x from
matches as M1, matches as MX
where M1.result=1 and MX.result=x) M1x
on (M.mult=M1x.mult_1 and M.result=1) or (M.mult=M1x.mult_x and result=x)
group by mult_1, mult_x
EDIT:
I'm supposing you wnat to get the count of all possible combinations of multipliers of matches with result=1 and matches with result = x.
If this is the case, M1x gives all this possible combinations. And you join all the possible matchea with have any of those combinations, and count them, grouping by the possible set of combinations defined by M1x.

Related

Create multiple rows based on 1 column

I currently have a table with a quantity in it.
ID Code Quantity
1 A 1
2 B 3
3 C 2
4 D 1
Is there anyway to write a sql statement that would get me
ID Code Quantity
1 A 1
2 B 1
2 B 1
2 B 1
3 C 1
3 C 1
4 D 1
I need to break out the quantity and have that many number of rows
Thanks
Here's one option using a numbers table to join to:
with numberstable as (
select 1 AS Number
union all
select Number + 1 from numberstable where Number<100
)
select t.id, t.code, 1
from yourtable t
join numberstable n on t.quantity >= n.number
order by t.id
Online Demo
Please note, depending on which database you are using, this may not be the correct approach to creating the numbers table. This works in most databases supporting common table expressions. But the key to the answer is the join and the on criteria.
One way would be to generate an array with X elements (where X is the quantity). So for rows
ID Code Quantity
1 A 1
2 B 3
3 C 2
you would get
ID Code Quantity ArrayVar
1 A 1 [1]
2 B 3 [1,2,3]
3 C 2 [2]
using a sequence function (e.g, in PrestoDB, sequence(start, stop) -> array(bigint))
Then, unnest the array, so for each ID, you get a X rows, and set the quantity to 1. Not sure what SQL distribution you're using, but this should work!
You can use connect by statement to cross join tables in order to get your desired output.
check my solution it works pretty robust.
select
"ID",
"Code",
1 QUANTITY
from Table1, table(cast(multiset
(select level from dual
connect by level <= Table1."Quantity") as sys.OdciNumberList));

Delete rows, which are duplicated and follow each other consequently

It's hard to formulate, so i'll just show an example and you are welcome to edit my question and title.
Suppose, i have a table
flag id value datetime
0 b 1 343 13
1 a 1 23 12
2 b 1 21 11
3 b 1 32 10
4 c 2 43 11
5 d 2 43 10
6 d 2 32 9
7 c 2 1 8
For each id i want to squeze the table by flag columns such that all duplicate flag values that follow each other collapse to one row with sum aggregation. Desired result:
flag id value
0 b 1 343
1 a 1 23
2 b 1 53
3 c 2 75
4 d 2 32
5 c 2 1
P.S: I found functions like CONDITIONAL_CHANGE_EVENT, which seem to be able to do that, but the examples of them in docs dont work for me
Use the differnece of row number approach to assign groups based on consecutive row flags being the same. Thereafter use a running sum.
select distinct id,flag,sum(value) over(partition by id,grp) as finalvalue
from (
select t.*,row_number() over(partition by id order by datetime)-row_number() over(partition by id,flag order by datetime) as grp
from tbl t
) t
Here's an approach which uses CONDITIONAL_CHANGE_EVENT:
select
flag,
id,
sum(value) value
from (
select
conditional_change_event(flag) over (order by datetime desc) part,
flag,
id,
value
from so
) t
group by part, flag, id
order by part;
The result is different from your desired result stated in the question because of order by datetime. Adding a separate column for the row number and sorting on that gives the correct result.

How to arrange Sql rows in a specific format

I have table with up to 50 rows... like given below.
ID menu dispOdr ParntID
---------------------
1 abc 1 0
2 cde 2 0
3 fgh 1 2
4 ghdfdj 2 2
5 tetss 1 1
6 uni 3 0
but I want to be sorted
Like
ID menu dispOdr ParntID
---------------------
1 abc 1 0
5 tetss 1 1
2 cde 2 0
3 fgh 1 2
4 ghdfdj 2 2
6 uni 3 0
If have any query please let me know.. thanks in advance.
I am using sql server 2014
I think you need your current vs. desired output reversed. You say you want the menu column sorted, but it appears that it already is.
So assuming you are actually starting with the second table, you can sort the menu column simply using ORDER BY:
SELECT *
FROM mytable
ORDER BY menu ASC
I think that the query below produces the required output:
SELECT t1.ID, t1.menu, t1.dispOdr, t1.ParntID
FROM mytable AS t1
LEFT JOIN mytable AS t2 ON t1.ParntID = t2.ID
ORDER BY CASE
WHEN t1.ParntID = 0 THEN t1.dispOdr
ELSE t2.dispOdr
END,
CASE
WHEN t1.ParntID = 0 THEN 1
ELSE 2
END,
t1.dispOdr
The first CASE expression groups records according to the dispOdr of their parent. The second CASE places parent on the top of its subgroup. Finally, the last expression used in the ORDER BY clause orders all child records within a subgroup.
Note: The above query works with one level of nesting.

count(*) from 2 tables with same columns

I have a weird problem at hand and I am sure I am very bad at SQL.
The problem is as follows:
I have 2 tables table1 and table2. Both the tables have same set of columns namely ID number, X number, Y number. Here X and Y will have value 0 or 1.
Now say for example ID ranges from 1-100 in table1 and ranges from 91-200 in table2: in table1, X has values for all the 100 rows say 1, and in the same table Y has values for only 90 say 1. The next 10 values of Y that is 91-100 are in table2.
Now, calling a count(*) for various queries say X=1 and Y=1, X=1 and Y=0 etc I do not get the correct values because some set of Y values are present in table2. I was looking at left Join but somehow I am unable to figure out if this is the correct approach.
Table1
-------
Id X Y
1 1 1
2 1 1
3 1 1
4 1 0
5 1 0
Table2
-------
Id X Y
4 0 1
5 0 1
6 0 0
7 0 0
8 0 0
9 1 1
So If I say X=1 and Y=1 I should get 5 as the count(*).
Hi Justin,
Let me explain you the actual scenario. Consider 3 processes p1,p2 and p3 which have a cache where P1, P2 and P3 are nothing but X,Y and Z columns. The content of this cache is nothing but the ID's. IF p1 dumps and Id 1 I would say X=1 for ID=1 etc. Each of these processes dumps cache in a group say g1 and g2. I have created a table with respect to g1 and g2. So g1 represent table1 and g2 represent table2. Each of p1, p2 and p3 have a limit of say dumping 100 IDs. It is possible that P1 has dumped ID's 1-100 in g1(table1) where p2 has dumped just 90 in g1(table1) and rest 10 in g2(table2), similarly p3 would have say dumped 95 in g1(table1) and rest 5 in g2(table2). However each of p1, p2 and p3 have dumped 100 ids but in different groups. Now if I want to get a count() in a ideal case when all the P1, P2 and P3 have dumped cache in g1 I would say get me max(id) from g1 where P1=1 and similarly min(id) from g1 where P1=1. Than I would write a query saying "Select count() from g1 where X=1 and Y=1 and Z=1 where ID between min(id) from g1 and max(id) from g1. In an ideal case it would have returned 100.But in current case it returns 90 which is not correct. So to resolve this issue I will also have to consider the ID's which are present in g2(table2) also.
I hope this answers your question.
Thanks
mav
It sounds like you want to UNION or UNION ALL the two tables together before applying the predicates. Something like
SELECT COUNT(*)
FROM (SELECT id, x, y
FROM table1
UNION ALL
SELECT id, x, y
FROM table2)
WHERE x = 1
AND y = 1;
UNION ALL will return every row from both tables. UNION will eliminate duplicate rows.
If this is not what you want, it would be very helpful to walk through an example where you create a few rows of sample data in each table and show us exactly the result you want and how you obtained that result.
SELECT COUNT(*)
FROM
(SELECT ID, MAX(X) X, MAX(Y) Y
FROM
(SELECT *
FROM TABLE1
UNION ALL
SELECT *
FROM TABLE2)
GROUP BY ID)
WHERE X = 1 AND Y = 1
Or if you wanna use an advanced group by clause
SELECT COUNT(*)
FROM
(SELECT *
FROM TABLE1
UNION ALL
SELECT *
FROM TABLE2)
HAVING MAX(X) = 1 AND MAX(Y) = 1
GROUP BY ID

Using random value as join condition

I am generating some test-data and use dbms_random. I encountered some strange behavior when using dbms_random in the condition of the JOIN, that I can not explain:
------------------------# test-data (ids 1 .. 3)
With x As (
Select Rownum id From dual
Connect By Rownum <= 3
)
------------------------# end of test-data
Select x.id,
x2.id id2
From x
Join x x2 On ( x2.id = Floor(dbms_random.value(1, 4)) )
Floor(dbms_random.value(1, 4) ) returns a random number out of (1,2,3), so I would have expected all rows of x to be joined with a random row of x2, or maybe always the same random row of x2 in case the random number is evaluated only once.
When trying several times, I get results like that, though:
(1) ID ID2 (2) ID ID2 (3)
---- ---- ---- ---- no rows selected.
1 2 1 3
1 3 2 3
2 2 3 3
2 3
3 2
3 3
What am I missing?
EDIT:
SELECT ROWNUM, FLOOR(dbms_random.VALUE (1, 4))
FROM dual CONNECT BY ROWNUM <= 3
would get the result in this case, but why does the original query behave like that?
To generate three rows with one predictable value and one random value, try this:
SQL> with x as (
2 select rownum id from dual
3 connect by rownum <= 3
4 )
5 , y as (
6 select floor(dbms_random.value(1, 4)) floor_val
7 from dual
8 )
9 select x.id,
10 y.floor_val
11 from x
12 cross join y
13 /
ID FLOOR_VAL
---------- ----------
1 2
2 3
3 2
SQL
edit
Why did your original query return an inconsistent set of rows?
Well, without the random bit in the ON clause your query was basically a CROSS JOIN of X against X - it would have returned nine rows (at least it would have if the syntax had allowed it). Each of those nine rows executes a call to DBMS_RANDOM.VALUE(). Only when the random value matches the current value of X2.ID is the row included in the result set. Consequently the query can return 0-9 rows, randomly.
Your solution is obviously simpler - I didn't refactor enough :)