Suppose I have a table that looks like this:
OrderNumber OrderType
1 D
1 D
1 R
2 D
2 R
3 D
3 D
3 D
3 R
3 R
The result should be:
OrderNumber OrderType
1 D
3 D
Here, an R would indicate to remove one row from the order. We see in the first example we have 2 D's and 1 R, so we remove one D are replaced with 1 D. Is there a way to do this in SQL?
If your mysql version support cte and window function, we can try to use ROW_NUMBER window function make row number for each OrderNumber OrderType
Then use EXISTS subquery to judge OrderType = D row number needs to be greater than the maximum row number from R.
with cte as (
SELECT *,
ROW_NUMBER() OVER(PARTITION BY OrderNumber,OrderType) rn,
COUNT(*) OVER(PARTITION BY OrderNumber,OrderType) cnt
FROM T
)
SELECT c1.OrderNumber,
c1.OrderType
FROM cte c1
WHERE EXISTS (
SELECT 1
FROM cte c2
WHERE c1.OrderNumber = c2.OrderNumber
AND c2.OrderType = 'R'
AND c1.rn > c2.cnt
)
AND c1.OrderType = 'D'
sqlfiddle
You can use window function. This is sqlite syntax, but mysql should be fairly close.
select A.OrderNumber,A.OrderType
from (select OrderNumber,OrderType,row_number() over(partition by OrderNumber) as RN
from b where OrderType='D') A
left join
(select sum(case when OrderType='R' then 1 else 0 end) as cnt,OrderNumber
from b group by OrderNumber) B
on A.OrderNumber=B.OrderNumber
where A.rn>B.cnt;
Related
cust_id
state
1
CA
2
IL
3
SC
1
PA
3
IA
4
MO
5
VA
4
NY
Can any one please advise on SQL Query that return the cust_id that belong to two states as below:
The output should be
cust_id
state
1
CA
1
PA
3
SC
3
IA
4
NY
4
MO
Try the following query
Solution 1
Select * From yourtable
Where Cust_Id In (Select cust_id From yourtable Group By cust_id Having
Count(*) = 2) Order By cust_id,state
Solution 2
With T1 As
(Select cust_id From yourtable Group By cust_id Having Count(*) = 2)
Select T2.* From yourtable T2 Join T1 On T1.cust_id = T2.cust_id O
Order By T2.cust_id,T2.state
SELECT tmp.*
FROM tmp
INNER JOIN (
SELECT cust_id
,COUNT(STATE) s_count
FROM tmp
GROUP BY [cust_id]
) sub
ON tmp.cust_id = sub.cust_id
WHERE sub.s_count = 2
ORDER BY cust_id
,STATE
One simple approach would use a COUNT window function, that will assign the amount of times each "cust_id" occurs in your table. Once you get this value, you can filter out rows whose count is smaller than 2.
WITH cte AS (
SELECT *, COUNT(cust_id) OVER(PARTITION BY cust_id) AS cnt
FROM tab
)
SELECT cust_id,
state
FROM cte
WHERE cnt > 1
Check the demo here.
I am trying to filter out some records based on condition but couldn't get the proper results.
Data:
GID OID SID Z
1 1 1 A
1 2 2 B
1 3 3 C
1 2 4 B
Expected Result:
GID OID SID Z
1 1 1 A
1 3 3 C
Here GID, OID can be repeated but not SID.
Need to filter out all records where Z contains 'A' & 'C'
What I have tried:
select distinct GID, OID, SID, Z
from table
where Z ilike ('A') or Z ilike ('C')
but this query will include all record of sample GID records.
Moreover I have also thought of self join but could not frame the query around that.
I think this is the query you need
WITH cte AS (
SELECT
gid, oid, sid, z,
ROW_NUMBER() OVER () AS rn
FROM data
WHERE LOWER(z) LIKE '%a%'
OR LOWER(Z) LIKE '%c%'
)
SELECT gid, oid, sid, z FROM cte c
WHERE NOT EXISTS (
SELECT sid FROM cte t
WHERE t.z = c.z
AND t.sid = c.sid
AND t.rn < c.rn
)
I use ROW_NUMBER to be able to check if a sid value repeats.
Demo
Please consider the table below
Id F1 F2
---------------------------
1 Nima a
2 Eli a
3 Arian a
4 Ava b
5 Arsha b
6 Rozhan c
7 Zhina c
I want to display records by sorting COLUMN F2 to display one record from each string category (a,b,c) in order
Id F1 F2
---------------------------
1 Nima a
5 Arsha b
6 Rozhan c
2 Eli a
4 Ava b
7 Zhina c
3 Arian a
NOTE: a,b,c could be anything... it should take one record from one entry and then 2nd from 2nd entry.
I have used join, or group by records but no success.
MySQL version 5.7 – Syed Saqlain
SELECT id, f1, f2
FROM ( SELECT t1.id, t1.f1, t1.f2, COUNT(*) cnt
FROM test t1
JOIN test t2 ON t1.f2 = t2.f2 AND t1.id >= t2.id
GROUP BY t1.id, t1.f1, t1.f2 ) t3
ORDER BY cnt, f2;
https://dbfiddle.uk/?rdbms=mysql_5.7&fiddle=8138bd9ab5be36ba534a258d20b2e555
ROW_NUMBER() alternative for lower version of MYSQL. This query will work for version 5.5, 5.6 & 5.7.
-- MySQL (v5.7)
SELECT t.id, t.f1, t.f2
FROM (SELECT #row_no:=CASE WHEN #db_names=d.f2 THEN #row_no+1 ELSE 1 END AS row_number
, #db_names:= d.f2
, d.f2
, d.f1
, d.id
FROM test d,
(SELECT #row_no := 0,#db_names:='') x
ORDER BY d.f2) t
ORDER BY t.row_number, t.f2
Please check from url https://dbfiddle.uk/?rdbms=mysql_5.7&fiddle=02dbb0086a6dd7c926d55a690bffbd06
You can use window functions in the order by:
select t.*
from t
order by row_number() over (partition by f2 order by id),
f2;
The row_number() function (as used above) assigns a sequential number starting with 1 to each value of f2.
In older versions of MySQL, you can use a correlated subquery instead:
order by (select count(*) from t t2 where t2.f2 = t.f2 and t2.id <= t.id),
f2;
For performance, you want an index on (f2, id).
I have the following query in MySQL:
select val,count(val)
from ....
where ...
group by val
It gives:
val count
CE3 4
CE5 1
A3 12
BRICK4 5
BRICK2 2
I want to show only the row with the highest count per first letter.
Which means all val starting with A are one group, all val starting with B are another group etc...
The expected result is:
val count
CE3 4 / CE3 CE5 are in group C , CE3 has higher count
A3 12 / A3 is the only one in group A
BRICK4 5 / BRICK4 BRICK2 are in group B, BRICK4 has higher count
How can I do that?
Edit:
what I thought to do is to create a temp column in a query that will represent the group something like:
val count group
CE3 4 C
CE5 1 C
A3 12 A
BRICK4 5 B
BRICK2 2 B
and then search for the row with the highest count value per group.
But i'm not sure this is the best approach
Try something like this:
select
val
,MAX(count) as count
,left(val,1) as first_letter
from (
select
val
,count(val) as count
from tbl
group by val
) a
group by left(val, 1);
First get count per val and from this result get the MAXcount grouping by first letter
UPDATE: (thx to Vamsi Prabhala for pointing it out that my first solution wasn't the best one)
After get the count per val, I used a variable to redo the ROW_NUMBER() functionality (from MS-SQL) and select the first row from result, ordered by first_letter and count desc
select val, count, first_letter from (
select
#i:=CASE
WHEN #first_letter = first_letter THEN #i + 1
ELSE 1
END as rn
,#first_letter:= a.first_letter as First_letter
,a.val
,a.count
from (
select
val
,count(val) as count
,left(val,1) as first_letter
from tbl
group by val
)a, (select #i:=0) b
order by First_letter, count desc
) c
where rn = 1
This can be done with variables to rank the rows based on counts.
select val,val_cnt
from (
select val,val_cnt,#rn:=case when #prev=left(val,1) then #rn+1 else 1 end as rnum,
#prev:=left(val,1)
from (select val,count(val) as val_cnt
from ....
where ...
group by val
) t
cross join (select #rn:=0,#prev:='') r
order by left(val,1),val_cnt desc,val --added val to order by to break ties
) t
where rnum=1
Not sure it's what you want :
Let's get the max from each starting letter :
SELECT LEFT(val, 1) as l_val, MAX(count_val)
FROM (
select val,count(val) as count_val
from ....
where ...
group by val
) t
GROUP BY l_val
THEN as you want the val to appear :
SELECT t2.val, t1.max_val
FROM (
SELECT LEFT(val, 1) as l_val, MAX(count_val) as max_val
FROM (
select val,count(val) as count_val
from ....
where ...
group by val
) t
GROUP BY l_val) t1
INNER JOIN `YOUR_table` t2 ON LEFT(t2.val,1) = t1.l_val
Does a relational database exist that has a GROUP BY aggregate function such as DISTINCT EXISTS that returns TRUE if there is more than one distinct value for the group and FALSE otherwise? I am looking for something that would iterate through the values in the group until the current value is not the same as the previous value, instead of counting ALL of the distinct values.
Example:
pv_name | time_stamp | value
A | 1 | 1
B | 2 | 1
C | 3 | 1
A | 4 | 2
C | 5 | 2
B | 6 | 3
SELECT pv_name
FROM example
WHERE time_stamp > 0 AND time_stamp < 6
GROUP BY pv_name
HAVING DISTINCT_EXISTS(value);
Result: A, C
SELECT pv_name
FROM example
WHERE time_stamp > 0 AND time_stamp < 6
GROUP BY pv_name
HAVING MIN(value)<>MAX(value);
Might get you there quicker depending on indexes. I don't think you'll do much better than this or COUNT(DISTINCT value) though.
Have you tried joining to example twice?
Psuedo-code example:
with
(
SELECT pv_name
FROM example
WHERE time_stamp > 0 AND time_stamp < 6
) as Q
select distinct Q1.pv_name
from Q as Q1 inner join Q as Q2 on
Q1.pv_name=Q2.pv_name and
Q1.value<>q2.value
You probably know about the COUNT(DISTINCT) function and you want to avoid it to prevent unnecessary computations.
It is hard to know why you are looking for this but I assume that it takes long time to find these groups using the most obvious query:
SELECT type, COUNT(DISTINCT product)
FROM aTable
GROUP BY type
HAVING COUNT(DISTINCT product) > 1
I can recommend you try the window functions. Try for example the new T-SQL's LAST_VALUE and FIRST_VALUE functions:
with c as (
SELECT type
,LAST_VALUE(product) OVER (PARTITION BY type ORDER BY product) lv
,FIRST_VALUE(product) OVER (PARTITION BY type ORDER BY product) pv
FROM aTable
)
SELECT * from c where lv <> pv
If the DB engine is smart enough it will find the first/last value for the group and will not try to count all the values, and therefore perform better.
For MySQL you can use helper variables to get the row_number per group based on the distinct values, something like this:
SELECT type, product
FROM (
SELECT #row_num := IF(#prev_type=type and #prev_prod=product,#row_num+1,1) AS RowNumber
,type
,product
,#prev_type := type
,#prev_prod := product
FROM Person,
(SELECT #row_num := 1) x,
(SELECT #prev_type := '') y,
(SELECT #prev_prod := '') z
ORDER BY type, product
) as a
WHERE RowNumber > 1
I think the having min (value) <> max (value) will be most efficient here. An alternative is:
Select distinct pv_name
From example e
Left join (
Select value
From example
Where ...
Group by value
Having count (*) = 1
) s on e.value = s.value
Where s.value is null
Or you could use NOT EXISTS against that subquery instead.
Include the relevant where clause in the sub query.