SQL order by can't find a solution - sql

I'm struggling here to do an order by in a query.
Imagine the following result of current query.
TableA.Id ---- TableB.TagName ---- TableB.TagValue
1----------------A---------------------------Customer A
1----------------B---------------------------Contract B
1----------------C---------------------------Product Z
2----------------A---------------------------Customer B
2----------------B---------------------------Contract C
2----------------C---------------------------Product Y
3----------------A---------------------------Customer C
3----------------B---------------------------Contract D
3----------------C---------------------------Product X
So the sort is dynamic and can be A, B, C asc or desc (TableB.TagName)
If user selects C ASC as sort, the result should be:
TableA.Id ---- TableB.TagName ---- TableB.TagValue
3----------------A---------------------------Customer C
3----------------B---------------------------Contract D
3----------------C---------------------------Product X
2----------------A---------------------------Customer B
2----------------B---------------------------Contract C
2----------------C---------------------------Product Y
1----------------A---------------------------Customer A
1----------------B---------------------------Contract B
1----------------C---------------------------Product Z
Thank you so much for your help

Oh, I see.
You can use a subquery in the order by:
select a.id, b.tagname, b.tagvalue,
max(case when b.tagname = #usertagname then b.tagvalue end) over (partition by a.id) as tagtagvalue
from tablea a join
tableb b
on a.tagid = b.tagid
order by tagtagvalue, a.id, b.tagname;
To get the descending sort you need a case expression in the `order by.

Related

SQL Finding duplicate values in two of the three columns of each row

Let's say we have three columns: A, B, and C.
I would like to filter the results as follows:
The values of A and B are the same (duplicated) for > 1 (more than 1) row, and the value of C is always different.
In the attached image, the values that appear selected would meet the conditions mentioned above.
What I've tried:
SELECT
a.notation as A, a.gene as B, b.id as C
FROM
`db-dummy`.sgdata c
join `db-dummy`.g_info a on a.rec_id = c.gen_id
join `db-dummy`.spec_data b on b.rec_id = c.spec_id GROUP BY A, B HAVING COUNT(*) > 1;
I thought that using GROUP BY and HAVING COUNT(*) > 1 I could get the desired result, but I get the following error:
SQL Error [1055] [42000]: (conn=1632) Expression #3 of SELECT list is not in GROUP BY clause and contains nonaggregated column 'db-dummy.b.spec_id' which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by
If you had a single table, I would suggest just using exists. But because you have a join, use window functions. If you are. looking for different values of id:
SELECT A, B, C
FROM (SELECT a.notation as A, a.gene as B, b.id as C,
MIN(b.id) OVER (PARTITION BY a.notation, a.gene) as min_id,
MAX(b.id) OVER (PARTITION BY a.notation, a.gene) as max_id
FROM `db-dummy`.sgdata c JOIN
`db-dummy`.g_info a
ON a.rec_id = c.gen_id JOIN
`db-dummy`.spec_data b
ON b.rec_id = c.spec_id
) x
WHERE min_id <> max_id;
If you are just looking for multiple rows for a given A and B, then you can use:
SELECT A, B, C
FROM (SELECT a.notation as A, a.gene as B, b.id as C,
COUNT(*) OVER (PARTITION BY a.noation, a.gene) as cnt
FROM `db-dummy`.sgdata c JOIN
`db-dummy`.g_info a
ON a.rec_id = c.gen_id JOIN
`db-dummy`.spec_data b
ON b.rec_id = c.spec_id
) x
WHERE cnt > 1;
SELECT * FROM `db-dummy`.sgdata a
LEFT JOIN
(SELECT COUNT(Id) as count, notation, gene
FROM `db-dummy`.sgdata
GROUP BY notation, gene
HAVING COUNT(id) > 1) b
on a.notation = b.notation AND a.gene = b.gene

How to find rows that have one equal value and one different value from the table

I have the following table:
ID Number Revision
x y 0
x y 1
z w 0
a w 0
a w 1
b m 0
b m 0
I need to return rows that for the same Number thare are more then one ID with the same Revision.Number can be "Null" and I don't need those values.
The output should be:
z w 0
a w 0
I have tried the following query:
SELECT a.id,a.number,a.revision,
FROM table a INNER JOIN
(SELECT id, number, revision FROM table where number > '0'
GROUP BY number HAVING COUNT(*) > 1
) b ON a.revision = b.revision AND a.id != b.id
A little addition- I have rows in my table with the same Number, ID and Revision- I don't need those rows in my query to be displayed!
It is not working! Please help me to figure out how to fix it.
Thanks.
Select t.Id,s.number,t.revision
from (Select number,count(*) 'c'
from table t1
where revision=0
group by number
having count(*) > 1
) s join table t on t.number= s.number
where revision = 0
Another simple approach:
SELECT DISTINCT b.id, b.Number, b.Revision
FROM tbl a
INNER JOIN tbl b
ON a.ID != b.ID AND a.Number = b.Number AND a.Revision = b.Revision;
This is tested in MySql 5, syntax might differ slightly.
You are not that far away with your query:
SELECT a.id,a.number,a.revision
FROM table a
JOIN (
-- multiple id for the same number and revision
SELECT number, revision
FROM table
GROUP BY number, revision
HAVING COUNT(*) > 1
) b
ON a.revision = b.revision
AND a.number = b.number
Untested, but you should get the idea. If your sql-server is a resent version you can solve this with OLAP functions as well.
To filter out rows where the whole row is duplicated we can select only unique rows via group by and having:
SELECT a.id,a.number,a.revision
FROM table a
JOIN (
-- multiple id for the same number and revision
SELECT number, revision
FROM table
GROUP BY number, revision
HAVING COUNT(*) > 1
) b
ON a.revision = b.revision
AND a.number = b.number
GROUP BY a.id,a.number,a.revision
HAVING COUNT(1) = 1

SQL move a duplicate value to a new column

I found a similar problem here: Move duplicated values into unique columns, but I'm not sure that is going to work for me.
The data provided looks like this:
email_address barcode value
1#aol.com 9X3BZBK9CPWVDT16 25
123#verizon.net 9X3L6RD1Y83J8Z6Q 50
123#verizon.net 9X3PWCQRNX2CNJBC 50
2#naver.com 9X3N438B6TZYRD1N 50
3#msn.com 9X3PVFJ2N3C2DNW6 25
456#yahoo.com 9X3BCJ3YXFQ1RFGU 50
456#yahoo.com 9X3L1DVG89CGDBB0 25
But I would like to have it look like this:
EMAIL_ADDRESS BURNED_BARCODE1 value1 BURNED_BARCODE2 value2
1#aol.com 9X3BZBK9CPWVDT16 25
123#verizon.net 9X3L6RD1Y83J8Z6Q 50 9X3PWCQRNX2CNJBC 50
2#naver.com 9X3N438B6TZYRD1N 50
3#msn.com 9X3PVFJ2N3C2DNW6 25
456#yahoo.com 9X3BCJ3YXFQ1RFGU 50 9X3L1DVG89CGDBB0 25
I am trying to consolidate the duplicate coupon codes and values into a BARCODE_2 and value2. I have thought about pivot tables but i cannot seem to get the logic to work.
Any thoughts? Thank you in advance!
You could use the ROW_NUMBER() function for this and a self-join:
;WITH cte AS (SELECT *, ROW_NUMBER()OVER(PARTITION BY email_address ORDER BY barcode) AS RN
FROM Table1)
SELECT a.email_address, a.barcode, a.value, b.barcode AS barcode2, b.value AS value2
FROM cte a
LEFT JOIN cte b
ON a.email_address = b.email_address
AND a.RN = b.RN -1
WHERE a.RN = 1
Demo: SQL Fiddle
Update: You could add a 3rd:
;WITH cte AS (SELECT *, ROW_NUMBER()OVER(PARTITION BY email_address ORDER BY barcode) AS RN
FROM Table1)
SELECT a.email_address, a.barcode, a.value
, b.barcode AS barcode2, b.value AS value2
, c.barcode AS barcode3, c.value AS value3
FROM cte a
LEFT JOIN cte b
ON a.email_address = b.email_address
AND a.RN = b.RN -1
LEFT JOIN cte c
ON a.email_address = c.email_address
AND a.RN = c.RN -2
WHERE a.RN = 1
Demo: SQL Fiddle
If going beyond 2 a PIVOT answer may be preferable.

Remove repeated values from the table

I have a PL/SQL select query like,
select
a.sgm,
b.numbr
from tbl1 a, tbl2 b
where b.itemId = a.itemId
and b.orgId = a.orgId
and a.srvCode = 'F'
and a.nbrCode <> 1
and rownum <= 7
Right now it retrieves like ,
sgm-|-numbr
-----------
abc-|-123
abc-|-678
abc-|-78
abc-|-099
bcd-|-153
bcd-|-123
bcd-|-123
I need to retrieve like ,
sgm-|-numbr
-----------
abc-|-123
bcd-|-153
ie, I need to remove the repeated ones in the first column. ie sgm shouldn't repeat.
Since you are using Oracle, then try this simplified version using a CTE:
WITH CTE as (
SELECT sgm, numbr,
rownum rn
FROM YourTable
)
SELECT CTE.sgm, CTE.numbr
FROM CTE
JOIN (
SELECT sgm, MIN(rownum) minrn
FROM CTE
GROUP BY sgm) t ON CTE.sgm = t.sgm AND CTE.rn = t.minrn
http://sqlfiddle.com/#!4/8d6fb/10
You can replace your query in the CTE above.
Good luck.
SELECT a.sgm, MAX(b.numbr)
FROM tbl1 a INNER JOIN tbl2 b
ON a.itemID = b.itemId
AND a.orgId = b.orgId
WHERE a.srvCode= 'F'
AND a.nbrCode <> 1
AND rownum <= 7
GROUP BY a.sgm
Apply a group function of your choice like MAX() on b.numbr, and apply the grouping on a.sgm, this should do what you need.
Advice : do your joins explicitly, see the difference between your query and mine.
select a.sgm,MAX(b.numbr)
from tbl1 a, tbl2 b
where b.itemId = a.itemId
AND b.orgId= a.orgId
and a.srvCode= 'F'
and a.nbrCode <> 1
and rownum<=7
group by sgm
The value of sgm wont repeat, but maximum value of number will be selected, similarly you can also select the minimum value using the Min function
Use group by function
select
a.sgm,
b.numbr
from tbl1 a, tbl2 b
where b.itemId = a.itemId
and b.orgId = a.orgId
and a.srvCode = 'F'
and a.nbrCode <> 1
and rownum <= 7
group by a.sgm
Select a from tbl a , tbl b WHERE a.userid > b..userid and
a.sgm = b.sgm;
Check this fiddle http://sqlfiddle.com/#!2/40b8f/2

Compare last to second last record for each contract

To keep it simple, my question is similar to THIS QUESTION, PART 2, only problem is, I am not running Oracle and thus can not use the rownumbers.
For those who need more information and examples:
I have a table
contractId date value
1 09/02/2011 A
1 13/02/2011 C
2 02/02/2011 D
2 08/02/2011 A
2 12/02/2011 C
3 22/01/2011 C
3 30/01/2011 B
3 12/02/2011 D
3 21/01/2011 A
EDIT: added another line for ContractID. Since I had some code myself, but that would display the following:
contractId date value value_old
1 09/02/2011 A
2 08/02/2011 A D
3 30/01/2011 B C
3 30/01/2011 B A
But that is not what I want ! The result should still be as below!
Now I want to select the last record before a given date and compare that with the previous value.
Suppose the 'given date' is 11/02/2011 in this example, the output should be like this:
contractId date value value_old
1 09/02/2011 A
2 08/02/2011 A D
3 30/01/2011 B C
I do have the query to select the last record before the given date. That is the easy part. But to select the last record before that, I am lost...
I really hope I can get some help here, have been breaking my head over this for days and looking for answers on the web and stackoverflow.
One possibility:
SELECT a.contractId, a.Date, a.Value, (SELECT Top 1 b.[Value]
FROM tbl b
WHERE b.[Date] < a.[Date] And b.ContractID=a.ContractID
ORDER BY b.[Date] Desc) AS Old_Value
FROM tbl AS a
WHERE a.Date IN
(SELECT TOP 1 b.Date
FROM tbl b
WHERE b.ContractID=a.ContractID
AND b.Date < #2011/02/11#
ORDER BY b.date DESC)
As promised, I would also post my answer. Although at this point, I still think Remou's answer is better, since the code is shorter and it seems more efficient (calls the same table fewer times). But here goes:
Query1:
SELECT c.contractID, c.firstofdates, a.value, d.value, d.date
FROM (table1 AS A RIGHT JOIN (SELECT b.cid,max(b.date) AS FirstOfdates
FROM table1 as B
where b.date < #02/11/2011#
GROUP BY b.contractID ) AS c ON (a.date = c.firstofdates) AND (a.contractID = c.contractID))
LEFT JOIN (select e.contractID, e.date, e.value
from table1 as e
) AS d ON (d.date < c.firstofdates) AND (d.contractID = c.contractID);
This query actually gives the result with the extra row for the 3rd contractID.
Query2:
SELECT b.contractID, max(a.date) AS olddate
FROM table1 AS a RIGHT JOIN (select contractID, firstofdates
from Query1) AS b ON (a.contractID= b.contractID) AND (a.date < b.firstofdates)
GROUP BY b.contractID;
And then to combine both:
Query3:
SELECT Query1.contractID, Query1.firstofdates AS [date], Query1.A.value AS [value], Query1.d.value AS [old value]
FROM Query1 RIGHT JOIN Query2
ON (Query1.date=Query2.olddate or Query2.olddate is null) AND (Query1.cid = Query2.cid);