SQL find missing language entries in table - sql

I have a table which is missing some entries for a certain language. How can I get a list of all language text in english (lang 1 in table) which is missing the foreign translation counterpart (lang 2)
My table is as follows
PageName | LanguageNo | TranslationName | TranslationText |
main | 1 | SomeName | some english text |
main | 2 | SomeName | some foreign text |
main | 1 | SomeName2 | some english 2 |
other | 1 | SomeName3 | some english 3 |
other | 2 | SomeName3 | some foreign 3 |
For example, using the above table data, only the following should be returned:
main | 1 | SomeName2 | some english 2 |
How can I write a SQL statement to achieve this?
Thanks

You can try the following:
-- Create demo data
CREATE TABLE #translation(pageName nvarchar(10), LanguageNo int, TranslationName nvarchar(25), TranslationText nvarchar(50))
INSERT INTO #translation(pageName, LanguageNo, TranslationName, TranslationText)
VALUES ('main',1,'SomeName','some english text'),
('main',2,'SomeName','some foreign text'),
('main',1,'SomeName2','some english 2'),
('other',1,'SomeName3','some english 3'),
('other',2,'SomeName3','some foreign 3')
--,('other',3,'SomeName3','some foreign 3') -- uncomment for language3 demo
-- your work:
SELECT availTrans.*
FROM #translation t
-- get all needed combinations
RIGHT JOIN(
SELECT DISTINCT t.pageName, t.TranslationName, langs.LanguageNo
FROM #translation as t
CROSS JOIN (SELECT DISTINCT LanguageNo FROM #translation) as langs
) as availTrans
ON t.pageName = availTrans.pageName
AND t.TranslationName = availTrans.TranslationName
AND t.LanguageNo = availTrans.LanguageNo
WHERE t.pageName IS NULL
-- Cleanup
DROP TABLE #translation
Given input:
pageName LanguageNo TranslationName TranslationText
---------- ----------- ------------------------- ---------------------
main 1 SomeName some english text
main 2 SomeName some foreign text
main 1 SomeName2 some english 2
other 1 SomeName3 some english 3
other 2 SomeName3 some foreign 3
Which produces this result:
pageName TranslationName LanguageNo
---------- ------------------------- -----------
main SomeName2 2

There are several methods, but here is one that uses not exists:
select t.*
from mytable t
where t.LanguageNo = 1 and
not exists (select 1
from mytable t2
where t2.pagename = t.pagename and
t2.translationname = t.translationname and
t2.LanguageNo = 2
);
Here is a SQL Fiddle.

You can also use SUM with OVER() like this.
Query
;WITH CTE AS
(
SELECT pageName, LanguageNo, TranslationName, TranslationText,
SUM(CASE WHEN LanguageNo = 1 THEN 1 ELSE 0 END) OVER(PARTITION BY TranslationName) L1,
SUM(CASE WHEN LanguageNo = 2 THEN 1 ELSE 0 END) OVER(PARTITION BY TranslationName) L2
FROM #translation t
)
SELECT pageName, LanguageNo, TranslationName, TranslationText FROM CTE
WHERE L1 >=1 AND L2 = 0
Output
pageName LanguageNo TranslationName TranslationText
main 1 SomeName2 some english 2

SELECT t1.*
FROM tblTranslation t1
LEFT JOIN tblTranslation t2
ON t2.TranslationName = t1.TranslationName
AND t2.LanguageNo = 2
WHERE t2.TranslationName IS NULL
AND t1.LanguageNo = 1

SELECT t1.*
FROM tblTranslation t1
LEFT JOIN tblTranslation t2 ON t2.PageName = t1.PageName
AND t2.TranslationName = t1.TranslationName
AND t2.LanguageNo = 2
WHERE t1.LanguageNo = 1
AND t2.LanguageNo IS NULL

Related

query select equaled data on three columns of table with four columns and ignore one

I have table have four columns like (right ,left ,up ,down) I want to build query that show equaled data on three of the four columns;
example:
| id | right | left | up | down |
|:---|:------:|:-----:|:------:| -----:|
| 1 | street |hospital|coffee |building|
| 2 | house |hospital|coffee |building|
| 3 | road | bus |coffee |sign |
| 4 | house |hospital|coffee |sign |
| 5 | car |road |coffee |sign |
the result should be like:
id
right
left
up
down
1
street
hospital
coffee
building
2
house
hospital
coffee
building
id number 3 and 5 not included because every column should equal it's self
is that query correct
select t.* from test_table t where
(t.right,t.left,t.up) in (select t.right,t.left,t.up from test_table t group by t.right,t.left,t.up having count(*)>1)
or (t.right,t.left,t.down) in (select t.right,t.left,t.down from test_table t group by t.right,t.left,t.down having count(*)>1)
or (t.right,t.up,t.down) in (select t.right,t.up,t.down from test_table t group by t.right,t.up,t.down having count(*)>1)
or (t.left,t.up,t.down) in (select t.left,t.up,t.down from test_table t group by t.left,t.up,t.down having count(*)>1)
and t.mud_id=285 order by t.right,t.left,t.up,t.down ;
if it correct it's go in loop without result for more than 10m waiting
if not
please what is the correct query to get the result
Since there are some assumptions to be made on how how to order and/or organize the results, you may use the query below to identify each matching pair.
DECLARE #T TABLE(ID INT, [right] VARCHAR(20), [left] varchar(20), up
varchar(20), down varchar(20))
INSERT INTO #T VALUES
(1,'street','hospital','coffee','building'),
(2,'house','hospital','coffee','building'),
(3,'road','bus','coffee','sign'),
(4,'house','hospital','coffee','sign'),
(5,'car','road','coffee','sign'),
(6,'street','road','coffee','sign')
SELECT
T1.ID AS MatchID1,
T2.ID AS MatchID2
FROM
#T T1
INNER JOIN #T T2 ON T1.ID <> T2.ID
GROUP BY
T1.ID, T2.ID
HAVING
MAX(CASE WHEN T1.[right] = T2.[right] THEN 1 ELSE 0 END +
CASE WHEN T1.[left] = T2.[left] THEN 1 ELSE 0 END +
CASE WHEN T1.up = T2.up THEN 1 ELSE 0 END +
CASE WHEN T1.down = T2.down THEN 1 ELSE 0 END) = 3
MatchID1 MatchID2
2 1
1 2
4 2
2 4
6 5
5 6
A trick to bring back matches by first occurrence using:
INNER JOIN #T T2 ON T1.ID > T2.ID
MatchID1 MatchID2
1 2
2 4
5 6

Select all records of one table that contain two records in another with certain id

I have two tables of 1:m relation. Need to select which People records have both records in Actions table whit id 1 and 2
People
+----+------+--------------+
| id | name | phone_number |
+----+------+--------------+
| 1 | John | 111111111111 |
+----+------+--------------+
| 3 | Jane | 222222222222 |
+----+------+--------------+
| 4 | Jack | 333333333333 |
+----+------+--------------+
Action
+----+------+------------+
| id | PplId| ActionId |
+----+------+------------+
| 1 | 1 | 1 |
+----+------+------------+
| 2 | 1 | 2 |
+----+------+------------+
| 3 | 2 | 1 |
+----+------+------------+
| 4 | 4 | 2 |
+----+------+------------+
Output
+----+------+--------------+----------
|PplId| name | Phone |ActionId |
+-----+------+-------------+----+-----
| 1 | John | 111111111111| 1 |
+-----+------+-------------+----+-----
| 1 | John | 111111111111| 2 |
+-----+------+-------------+----+-----
Return records of People that have both Have Actionid 1 and Action id 2(Have records in Actions).
Window functions are one method. Assuming actions are not duplicated for a person:
select pa.*
from (select p.*, a.action, count(*) over (partition by p.id) as num_actions
from people p join
action a
on p.id = a.pplid
where a.action in (1, 2)
) pa
where num_actions = 2;
In my opinion, getting two rows with the action detail seems superfluous -- you already know the actions. If you only want the people, then exists comes to mind:
select p.*
from people p
where exists (select 1 from actions where a.pplid = p.id and a.action = 1) and
exists (select 1 from actions where a.pplid = p.id and a.action = 2);
With the right index (actions(pplid, action)), I would expect two exists to be faster than group by.
Try this below query using subquery and join
select a.Pplid, name, phone, actionid from (
select a.pplid as Pplid, name, phone_number as phone
from People P
join Action A on a.pplid= p.id
group by a.pplid, name, phone_number
having count(*)>1 )P
join Action A on a.Pplid= p.Pplid
Try something like this
IF OBJECT_ID('tempdb..#People') IS NOT NULL DROP TABLE #People
CREATE TABLE #People (id INT, name VARCHAR(255), phone_number VARCHAR(50))
INSERT #People
SELECT 1, 'John', '111111111111' UNION ALL
SELECT 3, 'Jane', '222222222222' UNION ALL
SELECT 4, 'Jack', '333333333333'
IF OBJECT_ID('tempdb..#Action') IS NOT NULL DROP TABLE #Action
CREATE TABLE #Action (id INT, PplId INT, ActionId INT)
INSERT #Action
SELECT 1, 1, 1 UNION ALL
SELECT 2, 1, 2 UNION ALL
SELECT 3, 2, 1 UNION ALL
SELECT 4, 4, 2
GO
SELECT p.ID AS PplId
, p.name
, p.phone_number AS Phone
, a.ActionId
FROM #People p
JOIN #Action a
ON p.ID = a.PplId
WHERE p.ID IN ( SELECT PplId
FROM #Action
WHERE ActionId IN (1, 2)
GROUP BY PplId
HAVING COUNT(*) = 2 )
AND a.ActionId IN (1, 2)
GO

Change Position of Serial Number in SQL

I have a table named students. the structure is given below
______________________________
AdmissionNo RollNo Name
______________________________
1001 1 A
1003 2 B
1005 3 C
1006 4 D
1008 5 E
Now i want to change rollno 4 to 2 and increment forthcoming numbers
so the result should be like below
-------------------------------
AdmissionNo RollNo Name
-------------------------------
1001 1 A
1006 2 D
1003 3 B
1005 4 C
1008 5 E
--------------------------------
How to attain this using sql Query.
Note: Question Edited as per 'The Impaler' said.Admission number is not changing.only Roll no change. The values in table are examples actual values are hundreds of records.
With the omission of a dialect, I have answered this in T-SQL, as I wanted a stab at this.
This isn't pretty, however, I use a couple of updatable CTE's to find the offset for the specific rows, and then update the needed rows accordingly:
USE Sandbox;
GO
CREATE TABLE dbo.YourTable (AdmissionNo int, Rollno tinyint, [Name] char(1));
INSERT INTO dbo.YourTable
VALUES(1001,1,'A'),
(1003,2,'B'),
(1005,3,'C'),
(1006,4,'D'),
(1008,5,'E');
GO
DECLARE #NewPosition tinyint = 2,
#MovingName char(1) = 'D';
WITH Offsetting AS(
SELECT *,
COUNT(CASE Rollno WHEN #NewPosition THEN 1 END) OVER (ORDER BY RollNo ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING) -
COUNT(CASE [Name] WHEN #MovingName THEN 1 END) OVER (ORDER BY RollNo ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING) AS LagOffset
FROM dbo.YourTable),
NewNames AS(
SELECT *,
CASE RollNo WHEN #NewPosition THEN #MovingName
ELSE LAG([Name],LagOffset) OVER (ORDER BY RollNo)
END AS NewName
FROM Offsetting)
UPDATE NewNames
SET [Name] = NewName;
GO
SELECT *
FROM dbo.YourTable;
GO
DROP TABLE dbo.YourTable;
Not pretty but you could use some sub queries
DROP TABLE IF EXISTS T;
create table t
(AdmissionNo int, RollNo int, Name varchar(1));
insert into t values
(1001 , 1 , 'A'),
(1003 , 2 , 'B'),
(1005 , 3 , 'C'),
(1006 , 4 , 'D'),
(1008 , 5 , 'E');
select t.*,
case when rollno = 2 then (select name from t where rollno = 4)
when rollno > 2 and
rollno <> (select max(rollno) from t) then (select name from t t1 where t1.rollno < t.rollno order by t1.rollno desc limit 1)
else name
end
from t;
+-------------+--------+------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| 1001 | 1 | A | A |
| 1003 | 2 | B | D |
| 1005 | 3 | C | B |
| 1006 | 4 | D | C |
| 1008 | 5 | E | E |
+-------------+--------+------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
5 rows in set (0.001 sec)
DROP TABLE IF EXISTS my_table;
CREATE TABLE my_table
(admission_no INT NOT NULL PRIMARY KEY
,roll_no INT NOT NULL
,name CHAR(1) NOT NULL
);
INSERT INTO my_table VALUES
(1001,1,'A'),
(1003,2,'B'),
(1005,3,'C'),
(1006,4,'D'),
(1008,5,'E');
SELECT *
, CASE WHEN roll_no = 4 THEN 2
WHEN roll_no >= 2 AND roll_no < 4 THEN roll_no + 1
ELSE roll_no END x FROM my_table;
+--------------+---------+------+---+
| admission_no | roll_no | name | x |
+--------------+---------+------+---+
| 1001 | 1 | A | 1 |
| 1003 | 2 | B | 3 |
| 1005 | 3 | C | 4 |
| 1006 | 4 | D | 2 |
| 1008 | 5 | E | 5 |
+--------------+---------+------+---+
5 rows in set (0.00 sec)
...or, as an update...
UPDATE my_table x
JOIN
( SELECT *
, CASE WHEN roll_no = 4 THEN 2
WHEN roll_no >= 2 AND roll_no < 4 THEN roll_no + 1
ELSE roll_no END n
FROM my_table
) y
ON y.admission_no = x.admission_no
SET x.admission_no = y.n;
You'd probably want to extend this idea to deal with the fact that rows can be dragged up and down the list, so something like this...
SET #source = 1, #target = 5;
SELECT *
, CASE WHEN roll_no = GREATEST(#source,#target) THEN LEAST(#source,#target)
WHEN roll_no >= LEAST(#source,#target) AND roll_no < GREATEST(#source,#target) THEN roll_no + 1
ELSE roll_no END x
FROM my_table;
Try this below query
; with cte as (select a.AdmissionNo, a.RollNo, b.Name from student a
join student b on a.RollNo=b.RollNo+1
where a.RollNo between 3 and 4
union all
select a.AdmissionNo, a.RollNo, b.Name from student a
left join student b on a.RollNo+2=b.RollNo
where a.RollNo=2)
update a set a.Name = b.name
from student a
join cte b on a.rollno=b.rollno

SQL select all rows in a single row's "history"

I have a table that looks like this:
ID | PARENT_ID
--------------
0 | NULL
1 | 0
2 | NULL
3 | 1
4 | 2
5 | 4
6 | 3
Being an SQL noob, I'm not sure if I can accomplish what I would like in a single command.
What I would like is to start at row 6, and recursively follow the "history", using the PARENT_ID column to reference the ID column.
The result (in my mind) should look something like:
6|3
3|1
1|0
0|NULL
I already tried something like this:
SELECT T1.ID
FROM Table T1, Table T2
WHERE T1.ID = 6
OR T1.PARENT_ID = T2.PARENT_ID;
but that just gave me a strange result.
With a recursive cte.
If you want to start from the maximum id:
with recursive cte (id, parent_id) as (
select t.*
from (
select *
from tablename
order by id desc
limit 1
) t
union all
select t.*
from tablename t inner join cte c
on t.id = c.parent_id
)
select * from cte
See the demo.
If you want to start specifically from id = 6:
with recursive cte (id, parent_id) as (
select *
from tablename
where id = 6
union all
select t.*
from tablename t inner join cte c
on t.id = c.parent_id
)
select * from cte;
See the demo.
Results:
| id | parent_id |
| --- | --------- |
| 6 | 3 |
| 3 | 1 |
| 1 | 0 |
| 0 | |

Count missing values

I have a following table called Test:
Id | SomeId | Value
-----------------------------------------------------
019D9E52-41D1-45DF-81B6-C7CC484115A7 | 1 | 1
262640CA-65C2-4E30-8654-E187ACA1EEF4 | 1 | 1
53710AFC-4E19-4B1C-B68B-CDB713EC3D62 | 1 | 2
8FF7E77C-D04C-4961-82D9-87C2E5A1A096 | 1 | 2
-----------------------------------------------------
119D9E52-41D1-45DF-81B6-C7CC484115A7 | 2 | 1
762640CA-65C2-4E30-8654-E187ACA1EEF4 | 2 | 1
93710AFC-4E19-4B1C-B68B-CDB713EC3D62 | 2 | 2
4FF7E77C-D04C-4961-82D9-87C2E5A1A096 | 2 | 2
And there is a view called TestView:
SomeId | Value | Description
----------------------------
1 | 1 | 'One'
1 | 2 | 'Two'
1 | 3 | 'Three'
----------------------------
2 | 1 | 'One'
2 | 2 | 'Two'
These are just pseudo code examples.
I want to count all the values from the Test table (for a specific [SomeId]), and if value from the TestView (with a specific [SomeId]) is not in the Test table I just want to display 0 as count.
If I wanted to count values WHERE [Test].[SomeId] = 1, here's the expected result:
Value | Count
-----------------
One | 2
Two | 2
Three | 0
This is my query so far:
SELECT
tv.[Description] AS [Value],
COUNT(t.[Id]) - COUNT(tv.[Value]) AS [Count]
FROM [TestView] AS tv
LEFT JOIN [Test] AS t ON
t.[SomeId] = tv.[SomeId]
AND t.[Value] = tv.[Value]
WHERE
t.[SomeId] = 1
GROUP BY
tv.[Description]
But this gives me bad result... Anyways, here's the SQL Fiddle
EDIT:
This is just an addition to a Test table. What is Test table has one more foreign key Id, let's call it OtherId. Now when I use the query from the answer I won't get the result I wanted. Here's the modified query:
SELECT
t1.Description AS Value,
COUNT(t2.Value) AS Count
FROM TestView t1
LEFT JOIN test t2
ON t1.Value = t2.Value AND t1.SomeId = t2.SomeId
WHERE t1.SomeId = 1
AND t2.[OtherId] = *something* -- this is the addition
GROUP BY t1.Value, t1.Description
ORDER BY t1.Value;
Try this:
SELECT
t1.Description AS Value,
COUNT(t2.Value) AS Count
FROM TestView t1
LEFT JOIN test t2
ON t1.Value = t2.Value AND t1.SomeId = t2.SomeId
WHERE t1.SomeId = 1
GROUP BY t1.Value, t1.Description
ORDER BY t1.Value;
Demo
Below is your Solution
SELECT
tv.[Description] AS [Value],
COUNT(t.[Id]) AS [Count]
FROM [TestView] AS tv
LEFT OUTER JOIN [Test] AS t ON tv.SomeId = t.SomeId
AND t.Value = tv.value
AND t.[SomeId] = 1
GROUP BY
tv.[Description]