How to comapre two columns of a table in sql? - sql

In a table there are two columns:
-----------
| A | B |
-----------
| 1 | 5 |
| 2 | 1 |
| 3 | 2 |
| 4 | 1 |
-----------
Want a table where if A=B then
-------------------
|Match | notMatch|
-------------------
| 1 | 5 |
| 2 | 3 |
| Null | 4 |
-------------------
How can i do this?
I tried something which shows the Matched part
select distinct C.A as A from Table c inner join Table d on c.A=d.B

Try this:
;WITH TempTable(A, B) AS(
SELECT 1, 5 UNION ALL
SELECT 2, 1 UNION ALL
SELECT 3, 2 UNION ALL
SELECT 4, 1
)
,CTE(Val) AS(
SELECT A FROM TempTable UNION ALL
SELECT B FROM TempTable
)
,Match AS(
SELECT
Rn = ROW_NUMBER() OVER(ORDER BY Val),
Val
FROM CTE c
GROUP BY Val
HAVING COUNT(Val) > 1
)
,NotMatch AS(
SELECT
Rn = ROW_NUMBER() OVER(ORDER BY Val),
Val
FROM CTE c
GROUP BY Val
HAVING COUNT(Val) = 1
)
SELECT
Match = m.Val,
NotMatch= n.Val
FROM Match m
FULL JOIN NotMatch n
ON n.Rn = m.Rn

Try with EXCEPT, MINUS and INTERSECT Statements.
like this:
SELECT A FROM TABLE1 INTERSECT SELECT B FROM TABLE1;

You might want this:
SELECT DISTINCT
C.A as A
FROM
Table c
LEFT OUTER JOIN
Table d
ON
c.A=d.B
WHERE
d.ID IS NULL
Please Note that I use d.ID as an example because I don't see your schema. An alternate is to explicitly state all d.columns IS NULL in WHERE clause.

Your requirement is kind of - let's call it - interesting. Here is a way to solve it using pivot. Personally I would have chosen a different table structure and another way to select data:
Test data:
DECLARE #t table(A TINYINT, B TINYINT)
INSERT #t values
(1,5),(2,1),
(3,2),(4,1)
Query:
;WITH B AS
(
( SELECT A FROM #t
EXCEPT
SELECT B FROM #t)
UNION ALL
( SELECT B FROM #t
EXCEPT
SELECT A FROM #t)
), A AS
(
SELECT A val
FROM #t
INTERSECT
SELECT B
FROM #t
), combine as
(
SELECT val, 'A' col, row_number() over (order by (select 1)) rn FROM A
UNION ALL
SELECT A, 'B' col, row_number() over (order by (select 1)) rn
FROM B
)
SELECT [A], [B]
FROM combine
PIVOT (MAX(val) FOR [col] IN ([A], [B])) AS pvt
Result:
A B
1 3
2 4
NULL 5

Related

Getting all the values in one query that aren't in another with a group by

Given that I am using Redshift, how would I get the counts for a query that asks:
Given table A and table B, give me all the count of values in Table A for that grouping that aren't in table B;
So if table A and B look like:
Table A
Id | Value
==========
1 | "A"
1 | "B"
2 | "C"
And table B:
Id | Value
==========
1 | "A"
1 | "D"
2 | "C"
I would want:
Id | Count
==========
1 | 1
2 | 0
You can use left join and group by:
select a.id, sum( (b.id is null)::int )
from a left join
b
on a.id = b.id and a.value = b.value
group by a.id;
Use except and subquery
with a as
(
select 1 as id, 'A' as v
union all
select 1,'B'
union all
select 2,'C'
),b as
(
select 1 as id, 'A' as v
union all
select 1,'D'
union all
select 2,'C'
), c as
(
select id,v from a except select id,v from b
)
select id,sum ( (select count(*) from c where c.id=a.id and c.v=a.v))
from a group by id
output
id cnt
1 1
2 0
online demo which will work in redshift

Remove duplicate rows from joined table

I have following sql query
SELECT m.School, c.avgscore
FROM postswithratings c
join ZEntrycriteria on c.fk_postID= m.schoolcode
Which provide following result
School| avgscore
xyz | 5
xyz | 5
xyz | 5
abc | 3
abc | 3
kkk | 1
My question is how to remove those duplicates and get only following.
School| avgscore
xyz | 5
abc | 3
kkk | 1
I tried with
SELECT m.School, c.avgscore
FROM postswithratings c
join ZEntrycriteria on c.fk_postID= m.schoolcode
group by m.School
But it gives me following error
"Column 'postswithratings.avgscore' is invalid in the select list
because it is not contained in either an aggregate function or the
GROUP BY clause."
No need to make things complicated. Just go with:
SELECT m.School, c.avgscore
FROM postswithratings c
join ZEntrycriteria on c.fk_postID= m.schoolcode
group by m.School, c.avgscore
or
SELECT DISTINCT m.School, c.avgscore
FROM postswithratings c
join ZEntrycriteria on c.fk_postID= m.schoolcode
You have to only add distinct keyword like this :-
SELECT DISTINCT m.School, c.avgscore
FROM postswithratings c
join ZEntrycriteria on c.fk_postID= m.schoolcode
CREATE TABLE #Table2
([School] varchar(3), [avgscore] int)
INSERT INTO #Table2
([School], [avgscore])
VALUES
('xyz', 5),
('xyz', 5),
('xyz', 5),
('abc', 3),
('abc', 3),
('kkk', 1)
;
SELECT SCHOOL,AVGSCORE FROM (SELECT *,ROW_NUMBER() OVER( PARTITION BY [AVGSCORE] ORDER BY (SELECT NULL)) AS RN FROM #TABLE2)A
WHERE RN=1
ORDER BY AVGSCORE
-------
;WITH CTE AS
(SELECT *,ROW_NUMBER() OVER( PARTITION BY [AVGSCORE] ORDER BY (SELECT NULL)) AS RN FROM #TABLE2)
SELECT SCHOOL,AVGSCORE FROM CTE WHERE RN=1
output
SCHOOL AVGSCORE
kkk 1
abc 3
xyz 5
Using the DISTINCT keyword will make sql use sets instead of multisets. So values only appear once
This will delete the Duplicate rows (Only Duplicate)
Schema:
CREATE TABLE #TAB (School varchar(5) , avgscore int)
INSERT INTO #TAB
SELECT 'xyz', 5
UNION ALL
SELECT 'xyz', 5
UNION ALL
SELECT 'xyz', 5
UNION ALL
SELECT 'abc', 3
UNION ALL
SELECT 'abc', 3
UNION ALL
SELECT 'kkk', 1
Now use CTE as your Tempprary View and delete the data.
;WITH CTE AS(
SELECT ROW_NUMBER() OVER (PARTITION BY School,avgscore ORDER BY (SELECT 1)) DUP_C,
School, avgscore FROM #TAB
)
DELETE FROM CTE WHERE DUP_C>1
Now do check #TAB, the data will be
+--------+----------+
| School | avgscore |
+--------+----------+
| xyz | 5 |
| abc | 3 |
| kkk | 1 |
+--------+----------+
you only use group by if you're using aggregated function, eg. max. sum, avg
in that case,
SELECT Distinct(m.School), c.avgscore
FROM postswithratings c
join ZEntrycriteria on c.fk_postID= m.schoolcode

tSQL UNPIVOT of comma concatenated column into multiple rows

I have a table that has a value column. The value could be one value or it could be multiple values separated with a comma:
id | assess_id | question_key | item_value
---+-----------+--------------+-----------
1 | 859 | Cust_A_1 | 1,5
2 | 859 | Cust_B_1 | 2
I need to unpivot the data based on the item_value to look like this:
id | assess_id | question_key | item_value
---+-----------+--------------+-----------
1 | 859 | Cust_A_1 | 1
1 | 859 | Cust_A_1 | 5
2 | 859 | Cust_B_1 | 2
How does one do that in tSQL on SQL Server 2012?
We have a user defined function that we use for stuff like this that we called "split_delimiter":
CREATE FUNCTION [dbo].[split_delimiter](#delimited_string VARCHAR(8000), #delimiter_type CHAR(1))
RETURNS TABLE AS
RETURN
WITH cte10(num) AS
(
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
)
,cte100(num) AS
(
SELECT 1
FROM cte10 t1, cte10 t2
)
,cte10000(num) AS
(
SELECT 1
FROM cte100 t1, cte100 t2
)
,cte1(num) AS
(
SELECT TOP (ISNULL(DATALENGTH(#delimited_string),0)) ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
FROM cte10000
)
,cte2(num) AS
(
SELECT 1
UNION ALL
SELECT t.num+1
FROM cte1 t
WHERE SUBSTRING(#delimited_string,t.num,1) = #delimiter_type
)
,cte3(num,[len]) AS
(
SELECT t.num
,ISNULL(NULLIF(CHARINDEX(#delimiter_type,#delimited_string,t.num),0)-t.num,8000)
FROM cte2 t
)
SELECT delimited_item_num = ROW_NUMBER() OVER(ORDER BY t.num)
,delimited_value = SUBSTRING(#delimited_string, t.num, t.[len])
FROM cte3 t;
GO
It will take a varchar value up to 8000 characters and will return a table with the delimited elements broken into rows. In your example, you'll want to use an outer apply to turn those delimited values into separate rows:
SELECT my_table.id, my_table.assess_id, question_key, my_table.delimited_items.item_value
FROM my_table
OUTER APPLY(
SELECT delimited_value AS item_value
FROM my_database.dbo.split_delimiter(my_table.item_value, ',')
) AS delimited_items

Insert data into temp table from 2 source tables

I have 2 SELECT statements that both return 13 rows from dirrefernt tables
I would like to create 1 temporary table with 2 columns and insert the 2 result rows into the 2 columns. Is there a way to do this?
So
1 - SELECT INPOS FROM TABLE1 returns
1,2,3,4,5,6,7,18,9,10,11,12,13
2 - SELECT CODE FROM TABLE2 returns
CODEA,CODEB,CODEC,CODED,CODEE,CODEF,CODEG,CODEH,CODEI,CODEJ,CODEK,CODEL,CODEM
I would like my temporary table to be
1 | CODEA
2 | CODEB
3 | CODEC
4 | CODED
5 | CODEE
6 | CODEF
7 | CODEG
8 | CODEH
9 | CODEI
10 | CODEJ
11 | CODEK
12 | CODEL
13 | CODEM
Try this:
WITH T1 AS (
SELECT ROW_NUMBER() OVER(ORDER BY INPOS) ID, INPOS FROM TABLE1
),
WITH T2 AS
(
SELECT ROW_NUMBER() OVER(ORDER BY CODE) ID, CODE FROM TABLE2
),
SELECT T1.INPOS, T2.CODE
FROM T1 INNER JOIN T2 ON T1.ID = T2.ID
Try something like this:
SELECT a.impos, b.code
FROM (
(
SELECT impos, RANK() OVER (ORDER BY impos ASC) AS link
FROM table1
) AS a INNER JOIN (
SELECT code, RANK() OVER (ORDER BY code ASC) AS link
FROM table2
) AS b ON a.link = b.link
)
sqlfiddle demo

sql group by only rows which are in sequence

Say I have the following table:
MyTable
---------
| 1 | A |
| 2 | A |
| 3 | A |
| 4 | B |
| 5 | B |
| 6 | B |
| 7 | A |
| 8 | A |
---------
I need the sql query to output the following:
---------
| 3 | A |
| 3 | B |
| 2 | A |
---------
Basically I'm doing a group by but only for rows which are together in the sequence. Any ideas?
Note that the database is on sql server 2008. There is a post on this topic however it uses oracle's lag() function.
This is known as the "islands" problem. Using Itzik Ben Gan's approach:
;WITH YourTable AS
(
SELECT 1 AS N, 'A' AS C UNION ALL
SELECT 2 AS N, 'A' AS C UNION ALL
SELECT 3 AS N, 'A' AS C UNION ALL
SELECT 4 AS N, 'B' AS C UNION ALL
SELECT 5 AS N, 'B' AS C UNION ALL
SELECT 6 AS N, 'B' AS C UNION ALL
SELECT 7 AS N, 'A' AS C UNION ALL
SELECT 8 AS N, 'A' AS C
),
T
AS (SELECT N,
C,
DENSE_RANK() OVER (ORDER BY N) -
DENSE_RANK() OVER (PARTITION BY C ORDER BY N) AS Grp
FROM YourTable)
SELECT COUNT(*),
C
FROM T
GROUP BY C,
Grp
ORDER BY MIN(N)
this will work for you...
SELECT
Total=COUNT(*), C
FROM
(
SELECT
NGroup = ROW_NUMBER() OVER (ORDER BY N) - ROW_NUMBER() OVER (PARTITION BY C ORDER BY N),
N,
C
FROM MyTable
)RegroupedTable
GROUP BY C,NGroup
Just for fun, without any SQL-specific functions and NOT assuming that the ID column is monotonically increasing:
WITH starters(name, minid, maxid) AS (
SELECT
a.name, MIN(a.id), MAX(a.id)
FROM
mytable a RIGHT JOIN
mytable b ON
(a.name <> b.name AND a.id < b.id)
WHERE
a.id IS NOT NULL
GROUP BY
a.name
),
both(name, minid, maxid) AS (
SELECT
name, minid, maxid
FROM
starters
UNION ALL
SELECT
name, MIN(id), MAX(id)
FROM
mytable
WHERE
id > (SELECT MAX(maxid) from starters)
GROUP BY
name
)
SELECT
COUNT(*), m.name, minid
FROM
both INNER JOIN
mytable m ON
id BETWEEN minid AND maxid
GROUP BY
m.name, minid
Result (ignore the midid column):
(No column name) name minid
3 A 1
3 B 4
2 A 7