Select row of data based on specific conditions from other columns - sql

I have this data:
ID_OWN ID_PET KEY NAME
123 1 11 JOY
123 1 11 JOY
123 2 12 JOY
123 2 12 JOY
456 1 13 HELLO
456 1 13 MAMA
456 2 14 HELLO
456 2 14 MAMA
SCENARIO IS:
If I SELECT DISTINCT, ID_OWN 123 will return 2 rows of data.
But for ID_OWN 456, it will still return 4 rows of data, because column NAME has all different values.
What I want is, if NAME has different values under the same ID_OWN, then I want those NAME values to be sticked together, so it will return 2 rows of data too (or N rows of data based on no. of ID_PET under the same ID_OWN, in this case, they are 1 and 2)
Below is expected return data:
ID_OWN ID_PET KEY NAME
123 1 11 JOY
123 2 12 JOY
456 1 13 HELLO MAMA
456 2 14 HELLO MAMA

In SQL Server, You can achieve it in this way, demo on db<>fiddle
;WITH cte_TempTable AS (
SELECT DISTINCT ID_OWN, ID_PET, Key_Number, Name
FROM TempTable
)
SELECT DISTINCT ID_OWN, ID_PET, Key_Number, SUBSTRING(
(
SELECT ' ' + Name
FROM cte_TempTable C1
WHERE C1.ID_OWN = C2.ID_OWN AND C1.ID_PET = C2.ID_PET AND C1.Key_Number = C2.Key_Number
FOR XML PATH ('')
), 2, 1000) AS "nAME"
FROM cte_TempTable C2
Output
ID_OWN ID_PET Key_Number nAME
123 1 11 JOY
123 2 12 JOY
456 1 13 HELLO MAMA
456 2 14 HELLO MAMA

If it is MySQL, you can use GROUP_CONCAT as below-
DEMO HERE
SELECT ID_OWN,ID_PET,`Key`, group_CONCAT(NAME)
FROM (
SELECT DISTINCT ID_OWN,ID_PET,`Key`, NAME FROM your_table
)A
GROUP BY ID_OWN,ID_PET,`Key`

Use XML in combo with stuff
select distinct ID_OWN , ID_PET, [KEY],
STUFF((Select ' '+ NAME
from yourtable T1
where T1.ID_OWN =T2.ID_OWN and T1.ID_PET =T2.ID_PET and T1.[KEY] =T2.[KEY]
FOR XML PATH('')),1,1,'') as stickedName from yourtable T2
Output is:
ID_OWN ID_PET KEY stickedName
123 1 11 JOY
123 2 12 JOY
456 1 13 HELLO,MAMA
456 2 14 HELLO,MAMA

Related

SQL, label user based on the similarity

Is below case possible in SQL?
Let say I have a table like this:
user_id
product_id
1
123
1
122
1
121
2
124
2
125
2
121
3
123
3
122
3
122
4
123
4
212
4
222
5
124
5
125
5
121
I want to label the user if they have same product_id, regardless the order, so the output looks like this:
user_id
product_id
label
1
123
a
1
122
a
1
121
a
2
124
b
2
125
b
2
121
b
3
123
a
3
121
a
3
122
a
4
123
c
4
212
c
4
222
c
5
124
b
5
125
b
5
121
b
Please advise
You can use the string_agg function to get the list of product_ids for each user (as a single string), then use the dense_rank function on that string to get unique labels for each product_ids list.
select T.user_id, T.product_id, D.label
from table_name T join
(
select user_id,
chr(dense_rank() over (order by user_products) + 96) label
from
(
select user_id,
string_agg(cast(product_id as string), ',' order by product_id) user_products
from table_name
group by user_id
) lbl
) D
on T.user_id = D.user_id
order by T.user_id

Select a record a variable number of times based on two different fields on the same table

I'm not an expert of ANSI SQL and I need to write the following query.
This is the table from which I start:
ID
Max_Recurrency
Priority
abc
2
1
abc
2
450
abc
2
12
def
1
827
def
1
44
def
1
112
ghi
2
544
ghi
2
4
ghi
2
95
ghi
2
25
The output I need is something like this:
ID
Max_Recurrency
Priority
abc
2
450
abc
2
12
def
1
827
ghi
2
544
ghi
2
95
In other words, I need to select the ID of the record as many times as is indicated in the Max_Recurrency field and select the records with the highest Priority, i.e. excluding those with the lowest Priority if the Max_Recurrency field has a value less than the number of times the ID is repeated in the table.
Can anyone help me?
We can use ROW_NUMBER here:
WITH cte AS (
SELECT t.*, ROW_NUMBER() OVER (PARTITION BY ID ORDER BY Priority DESC) rn
FROM yourTable t
)
SELECT ID, Max_Recurrency, Priority
FROM cte
WHERE rn <= Max_Recurrency;

Select Data as per the given output

Table 1:
ID
CALLID
CALLSTATUS
1
123
Generated
2
321
Not Generated
3
343
Generated
4
567
Not Generated
5
789
Generated
Table 2:
UID
ID
CALLID
GENERATEDATE
RESULT
11
1
123
2021/3/18
1
21
1
123
2021/4/20
1
31
1
123
2021/5/20
0
41
2
321
NULL
NULL
51
3
343
2021/4/21
1
61
4
567
NULL
NULL
71
5
789
2021/5/1
0
Output of Oracle should be like below table:
ID
CALLID
GENERATEDATE
CALLSTATUS
RESULT
1
123
2021/4/20
Generated
1
2
321
NULL
Not Generated
NULL
3
343
2021/4/21
Generated
1
4
567
NULL
Not Generated
NULL
5
789
2021/5/1
Generated
0
The output which I want should be like above table. For CALLID '123', as per table 2 last generated call is on '2021/5/20' but it's result is '0' which i don't want to select. It should select '2021/4/21' because it's result is '1'. But for CALLID '789', No RESULT is '1' so it should select Generated Date as '2021/5/1'.
You can use conditional aggregation along with NVL() function depending on the values for the result column such as
SELECT t1.id, MAX(t1.callid) AS callid,
NVL(MAX(CASE WHEN result = 1 THEN t2.generatedate END),
MAX(CASE WHEN NVL(result,0)!=1 THEN t2.generatedate END)) AS generateddate,
MAX(t1.callstatus) AS callstatus, MAX(result) AS result
FROM Table1 t1
JOIN Table2 t2
ON t2.id = t1.id
AND t2.callid = t1.callid
GROUP BY t1.id
ORDER BY t1.id
ID
CALLID
GENERATEDDATE
CALLSTATUS
RESULT
1
123
20/04/2021
Generated
1
2
321
Not Generated
3
343
21/04/2021
Generated
1
4
567
Not Generated
5
789
01/05/2021
Generated
0
Demo

Reverse a number and sum of digits in sql

In sql database i have a table .In which I have column A is decimal(18,0) type.
A
34
123
345
879
I need column B and C as like this
B C
43 7
321 6
543 12
978 24
For Postgres you can use string_to_array() to split the number into digits:
with data (a) as (
values
(34),
(123),
(345),
(879)
)
select a,
string_agg(t.d::text, '' order by t.idx desc) as b,
sum(t.d::int) as c
from data,
unnest(string_to_array(a::text,null)) with ordinality as t(d, idx)
group by a;
The above returns:
a | b | c
----+-----+---
34 | 43 | 7
123 | 321 | 6
345 | 543 | 12
879 | 978 | 24
To get the reversed number, you could also use reverse() in Postgres
In Oracle, it can be done as follows -
SELECT
VALUE, REVERSE_VALUE, SUM(SUM_TOT) AS SUM
FROM (
SELECT
DISTINCT A AS VALUE, REVERSE(TO_CHAR(A)) AS REVERSE_VALUE, SUBSTR(TO_CHAR(A), LEVEL, 1) AS SUM_TOT
FROM (
SELECT 34 AS A FROM DUAL
UNION
SELECT 123 FROM DUAL
UNION
SELECT 345 FROM DUAL
UNION
SELECT 879 FROM DUAL
)
CONNECT BY LEVEL <= LENGTH(TO_CHAR(A))
ORDER BY 1
)
GROUP BY
VALUE, REVERSE_VALUE
;
Output -
VALUE|REVERSE_VALUE|SUM
34|43|7
345|543|12
123|321|6
879|978|24

Query to display unique comments based on certain conditions

TABLE DEFINITION
ColumnName Comments
CustomerID INT
SequenceNo INT
Comments VARCHAR(MAX)
CUSTOMER TABLE
CustomerID SequenceNo Comments
1 1 ABC D
1 2 CDE
1 3 ABC
1 4 ABC D
1 5 CDE
1 6 abc
2 7 ABC DEF
2 8
2 9 ABC DEF
2 10 DEF
2 11 XYZ 123
2 12 ABC
3 13 PQ RST
OUTPUT
CustomerID SequenceNo Comments
1 3 ABC
1 4 ABC D
1 5 CDE
1 6 abc
2 8
2 9 ABC DEF
2 10 DEF
2 11 XYZ 123
2 12 ABC
3 13 PQ RST
Records should be filtered by
1. Display only Unique Comments from Customer Table for all the customers,
2. If Comments are same then display the row which has maximum SequenceNo
This assumes you are using a case sensitive collation
SELECT CustomerID,
MAX(SequenceNo) AS SequenceNo,
Comments
FROM yourTable
GROUP BY CustomerID,Comments
ORDER BY CustomerID,MAX(SequenceNo)
If you are not using a case sensitive collation, then try this:
SELECT CustomerID,
MAX(SequenceNo) AS SequenceNo,
Comments COLLATE SQL_Latin1_General_CP1_CS_AS
FROM yourTable
GROUP BY CustomerID,Comments COLLATE SQL_Latin1_General_CP1_CS_AS
ORDER BY CustomerID,MAX(SequenceNo)
You can use window functions for this:
select c.*
from (select c.*,
row_number() over (partition by CustomerId, Comments
order by SequenceNo desc) as seqnum
from comments c
) c
where seqnum = 1;