I have a table in SQL with data and another table that holds the alias for that column. It is used for translation purposes.
I was wondering how can I do a select on those columns but retrieve the alias from another table?
This is the table that holds the real column names:
ID PageID ColName Order Type Width IsDeleted
1 7 CustType 2 NULL NULL 0
2 7 Description 3 NULL NULL 0
3 7 ApplyVAT 4 NULL NULL 0
4 7 ProduceInvoices 5 NULL NULL 0
5 7 PurchaseSale 6 NULL NULL 0
6 7 TermsDays 7 NULL NULL 0
7 7 DateTimeLastUpdated 8 NULL NULL 0
This is the table that holds the alias (text):
ID ColID UserID Text Order Enabled?
50 22 1 id 1 1
51 1 1 CustTypes 2 1
52 2 1 Description 3 1
53 3 1 ApplyVAT NULL 0
54 4 1 ProduceInvoices NULL 0
55 5 1 PurchaseSale NULL 0
56 6 1 TermsDays NULL 0
57 7 1 DateTimeLastUpdated NULL 0
I believe you will need to use dynamic sql to do this, e.g.:
DECLARE #Sql NVARCHAR(MAX);
SELECT TOP 1 #Sql = 'SELECT dt.ID as ' + at.IDAlias + ', dt.Town as ' + at.TownAlias
+ ' FROM DataTable dt'
FROM AliasTable at
WHERE at.LanguageID = 2;
EXEC(#Sql)
Given the example of Data Table
CREATE TABLE DataTable
(
ID INT,
Town NVARCHAR(50)
);
And a table holding language - dependent aliases for the columns in the above:
CREATE TABLE AliasTable
(
LanguageId INT,
IDAlias NVARCHAR(100),
TownAlias NVARCHAR(100)
);
SqlFiddle here
One of the (many) caveats with dynamic Sql is you will need to ensure that the alias data is validated against Sql Injectin attacks.
Related
We want to count how many nulls each column in a table has. There are too many columns to do this one by one, so the following PLSQL procedure was created.
In the first part of the procedure, all column names are obtained. This works, as the dbms_output correctly lists them all.
Secondly, a query inserts the count of null values in the variable 'nullscount'. This part does not work, as the output printed for this variable is always 0, even for columns where we know there are nulls.
Does anyone know how to handle the second part correctly?
Many thanks.
CREATE OR REPLACE PROCEDURE COUNTNULLS AS
nullscount int;
BEGIN
for c in (select column_name from all_tab_columns where table_name = upper('gp'))
loop
select count(*) into nullscount from gp where c.column_name is null;
dbms_output.put_line(c.column_name||' '||nullscount);
end loop;
END COUNTNULLS;
You can get it with just one query like this: this query scans table just once:
DBFiddle: https://dbfiddle.uk/asgrCezT
select *
from xmltable(
'/ROWSET/ROW/*'
passing
dbms_xmlgen.getxmltype(
(
select
'select '
||listagg('count(*)-count("'||column_name||'") as "'||column_name||'"',',')
||' from '||upper('gp')
from user_tab_columns
where table_name = upper('gp')
)
)
columns
column_name varchar2(30) path './name()',
cnt_nulls int path '.'
);
Results:
COLUMN_NAME CNT_NULLS
------------------------------ ----------
A 5
B 4
C 3
Dynamic sql in this query uses (24 chars + column name length) so it should work fine for example for 117 columns with average column name length = 10. If you need more, you can rewrite it a bit, for example:
select *
from xmltable(
'let $cnt := /ROWSET/ROW/CNT
for $r in /ROWSET/ROW/*[name() != "CNT"]
return <R name="{$r/name()}"> {$cnt - $r} </R>'
passing
dbms_xmlgen.getxmltype(
(
select
'select count(*) CNT,'
||listagg('count("'||column_name||'") as "'||column_name||'"',',')
||' from '||upper('gp')
from user_tab_columns
where table_name = upper('gp')
)
)
columns
column_name varchar2(30) path '#name',
cnt_nulls int path '.'
);
create table gp (
id number generated by default on null as identity
constraint gp_pk primary key,
c1 number,
c2 number,
c3 number,
c4 number,
c5 number
)
;
-- add some data with NULLS and numbers
DECLARE
BEGIN
FOR r IN 1 .. 20 LOOP
INSERT INTO gp (c1,c2,c3,c4,c5) VALUES
(CASE WHEN mod(r,2) = 0 THEN NULL ELSE mod(r,2) END
,CASE WHEN mod(r,3) = 0 THEN NULL ELSE mod(r,3) END
,CASE WHEN mod(r,4) = 0 THEN NULL ELSE mod(r,4) END
,CASE WHEN mod(r,5) = 0 THEN NULL ELSE mod(r,5) END
,5);
END LOOP;
END;
/
-- check what is in the table
SELECT * FROM gp;
-- do count of each column
DECLARE
l_colcount NUMBER;
l_statement VARCHAR2(100) := 'SELECT COUNT(*) FROM $TABLE_NAME$ WHERE $COLUMN_NAME$ IS NULL';
BEGIN
FOR r IN (SELECT column_name,table_name FROM user_tab_columns WHERE table_name = 'GP') LOOP
EXECUTE IMMEDIATE REPLACE(REPLACE(l_statement,'$TABLE_NAME$',r.table_name),'$COLUMN_NAME$',r.column_name) INTO l_colcount;
dbms_output.put_line('Table: '||r.table_name||', column'||r.column_name||', COUNT: '||l_colcount);
END LOOP;
END;
/
Table created.
Statement processed.
Result Set 4
ID C1 C2 C3 C4 C5
1 1 1 1 1 5
2 - 2 2 2 5
3 1 - 3 3 5
4 - 1 - 4 5
5 1 2 1 - 5
6 - - 2 1 5
7 1 1 3 2 5
8 - 2 - 3 5
9 1 - 1 4 5
10 - 1 2 - 5
11 1 2 3 1 5
12 - - - 2 5
13 1 1 1 3 5
14 - 2 2 4 5
15 1 - 3 - 5
16 - 1 - 1 5
17 1 2 1 2 5
18 - - 2 3 5
19 1 1 3 4 5
20 - 2 - - 5
20 rows selected.
Statement processed.
Table: GP, columnID, COUNT: 0
Table: GP, columnC1, COUNT: 10
Table: GP, columnC2, COUNT: 6
Table: GP, columnC3, COUNT: 5
Table: GP, columnC4, COUNT: 4
Table: GP, columnC5, COUNT: 0
c.column_name is never null because it's the content of the column "column_name" of the table "all_tab_columns"
not the column of which name is the value of c.column_name, in table gp.
You have to use dynamic query and EXECUTE IMMEDIATE to achieve what you want.
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 7 months ago.
Improve this question
I'm trying to pivot a synonyms table (all possible combinations) into an unknown number of columns (T-SQL). I'm using SQL Server 2017.
I have this table:
fd_Id
fd_Word
fd_Interpretation
1
smile
1
2
grin
1
3
laugh
1
4
see
2
5
detect
2
6
look
2
7
peek
2
8
walk
3
9
stroll
3
fd_Id is an identity column and synonyms are grouped by fd_Interpretation. While synonyms can have any number of rows (10+), in practice they are around 6 to 8.
This is the desired output:
Id
Word1
Word2
Word3
Word4
Wordn...
1
grin
laugh
smile
2
laugh
grin
smile
3
smile
grin
laugh
4
detect
look
peek
see
5
look
detect
peek
see
6
peek
detect
look
see
7
see
detect
look
peek
8
stroll
walk
9
walk
stroll
The idea is to get all words listed in the first column and its remaining synonyms on the columns to the right.
Any help is highly appreciated.
You can do this using dynamic pivot.
Note: But, I don't still understand, why you want to have all possible combinations of the same. It will lead to more complications and I have not addressed that in this solution.
CREATE table dbo.meaning (fd_Id int, fd_Word varchar(50), fd_Interpretation int)
insert into dbo.meaning values
(1,'smile ',1)
,(2,'grin ',1)
,(3,'laugh ',1)
,(4,'see ',2)
,(5,'detect ',2)
,(6,'look ',2)
,(7,'peek ',2)
,(8,'walk ',3)
,(9,'stroll ',3);
SELECT *,concat('word', cast(ROW_NUMBER() OVER (PARTITION BY FD_interpretation order by fd_word) as int)) as word FROM meaning
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX);
SET #cols = STUFF((SELECT distinct ',' + concat('word', cast(ROW_NUMBER() OVER (PARTITION BY FD_interpretation order by fd_Word) as int))
FROM dbo.meaning c
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT fd_interpretation,' + #cols + ' from
(
select fd_interpretation, fd_word, concat(''word'', cast(ROW_NUMBER() OVER (PARTITION BY FD_interpretation order by fd_Word) as int)) as word
from dbo.meaning
) x
pivot
(
max(fd_word)
for word in (' + #cols + ')
) p '
execute(#query)
fd_interpretation
word1
word2
word3
word4
1
grin
laugh
smile
NULL
2
detect
look
peek
see
3
stroll
walk
NULL
NULL
Solved it.
SELECT
[fd_Id] = ROW_NUMBER() OVER (ORDER BY pvt.[Keyword] ASC), *
FROM
(SELECT
src.[fd_Interpretation],
[Keyword] = src.[fd_Keyword],
[Item] = CONCAT('Synonym', ROW_NUMBER() OVER (PARTITION BY src.fd_Keyword ORDER BY src.fd_Keyword)),
[Value] = src.[fd_Synonym] --fd_Keyword
FROM
(
SELECT MAX(a.fd_Interpretation) AS [fd_Interpretation], MAX(u.fd_Keyword) AS [fd_Keyword], NULL AS [fd_Synonym]
FROM dbo.tb_Synonyms a INNER JOIN dbo.tb_UniqueKeywords u ON u.fd_Id = a.fd_Keyword
GROUP BY a.fd_Interpretation
HAVING COUNT(fd_Interpretation) = 1
UNION ALL
SELECT
a.fd_Interpretation, u.fd_Keyword, u1.fd_Keyword AS [fd_Synonym]
FROM
dbo.tb_Synonyms a INNER JOIN dbo.tb_UniqueKeywords u ON u.fd_Id = a.fd_Keyword
CROSS JOIN
dbo.tb_Synonyms b INNER JOIN dbo.tb_UniqueKeywords u1 ON u1.fd_Id = b.fd_Keyword
WHERE
a.fd_Interpretation = b.fd_Interpretation AND
a.fd_Keyword <> b.fd_Keyword) src
) src1
PIVOT
(MAX([Value]) FOR [Item] IN ([Synonym1], [Synonym2], [Synonym3], [Synonym4], [Synonym5], [Synonym6], [Synonym7], [Synonym8], [Synonym9], [Synonym10])) pvt;
Thanks to #john-cappelletti; used his answer to my other question and extended it to answer this one.
The actual words are on another table tb_UniqueKeywords (but that doesn't really change anything), as it functions as the original table in the question, as can be seen here:
SELECT
s.fd_Id,
u.fd_Keyword,
s.fd_Interpretation
FROM
tb_Synonyms s INNER JOIN
tb_UniqueKeywords u ON s.fd_Keyword = u.fd_Id
Output:
fd_Id fd_Keyword fd_Interpretation
----------- -------------------------------------------------- -----------------
3 2 1
5 Doble 1
4 Dos 1
6 Mayra 2
8 Vecina 2
7 5 3
13 Una 4
14 Unas 4
9 Uno 4
10 Unos 4
11 Oficina 5
12 Oficinas 5
It also correctly outputs words with no synonyms (as in the case of Keyword 5) due to the UNION ALL table which handles that.
Final output:
fd_Id fd_Interpretation Keyword Synonym1 Synonym2 Synonym3 Synonym4 Synonym5 Synonym6 Synonym7 Synonym8 Synonym9 Synonym10
-------------------- ----------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- --------------------------------------------------
1 1 2 Doble Dos NULL NULL NULL NULL NULL NULL NULL NULL
2 3 5 NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL
3 1 Doble 2 Dos NULL NULL NULL NULL NULL NULL NULL NULL
4 1 Dos 2 Doble NULL NULL NULL NULL NULL NULL NULL NULL
5 2 Mayra Vecina NULL NULL NULL NULL NULL NULL NULL NULL NULL
6 5 Oficina Oficinas NULL NULL NULL NULL NULL NULL NULL NULL NULL
7 5 Oficinas Oficina NULL NULL NULL NULL NULL NULL NULL NULL NULL
8 4 Una Unas Uno Unos NULL NULL NULL NULL NULL NULL NULL
9 4 Unas Una Uno Unos NULL NULL NULL NULL NULL NULL NULL
10 4 Uno Una Unas Unos NULL NULL NULL NULL NULL NULL NULL
11 4 Unos Una Unas Uno NULL NULL NULL NULL NULL NULL NULL
12 2 Vecina Mayra NULL NULL NULL NULL NULL NULL NULL NULL NULL
The USER table structure is
id owneeId delete shop
1 12 null 1
2 13 1 0
3 12 null 1
4 7 1 1
5 7 null 1
6 13 null 1
7 16 null 1
8 17 null 1
Now I am getting the result of ( query given below )
select * from users WHERE ownerId in ('12', '13') and delete is null or
ownerId not IN ('12','13') and shop=1 and delete is null
I will get all users with owner id 12 or 13 (with delete null) + all users with owner id not 12 or 13 + shop =1 and dlete is null.
Desired result
I will give another ownerID 7.
I need to get all the users with owner id 7 first, that means; from above table ,
all users with owner id 7 and shop =1 first ...only then all users with owner id 12 or 13 (with delete null) + all users with owner id not 12 or 13 + shop =1 and dlete is null
query will be something like
I need to add "select * from users where ownerId 7 first and shop 1 and delete null" to the query(above one, this one below- both same query)
select * from users WHERE ownerId in ('12', '13') and delete is null or
ownerId not IN ('12','13') and shop=1 and delete is null
** (PS)EDIT FOR BETTER CLARITY**
My current result is
id owneeId delete shop
1 12 null 1
2 12 null 1
2 7 null 1
2 13 null 1
2 16 null 1
2 17 null 1
what I need is- desired result
id owneeId delete shop
2 7 null 1
1 12 null 1
2 12 null 1
2 13 null 1
2 16 null 1
2 17 null 1
all user with ownerid 7 first , then the rest...( I am getting this result as an array of objects in node , in sql as table)
PS : Owner id can be any number, in this case it is 7. It can be 912, 989,89009,...etc
If you wish to just combine the 2 results, you can simply use union like below:
(
select *
from users
where ownerId = 7 and shop = 1
)
union
(
select *
from users
WHERE (ownerId in ('12', '13') and `delete` is null) or
(ownerId not IN ('12','13') and shop = 1 and `delete` is null)
)
Note: Using delete as one of the column names is not recommended since delete is a keyword in itself. You can rather go for deleted_at if it's a timestamp etc.
I have a scenario in left join of SQL which is not generating required output which i need. Following is description in tabular form and my tried queries,
Table A
A_ID // PK OF TABLE A
IS_ACTIVE // VALUE=1 OR 0
Table B
B_ID // PK OF TABLE B
A_ID // FK OF TABLE A IN TABLE B
Sample Records of Table A
A_ID IS_ACTIVE
1 1
2 0
3 1
4 0
5 0
Sample Records of Table B
B_ID A_ID
1 1
2 1
3 4
4 4
5 4
6 4
Select * from A left join B on A.A_ID=B.A_ID
A_ID IS_ACTIVE B_ID A_ID
1 1 1 1
1 1 2 1
2 0 NULL NULL
3 1 NULL NULL
4 0 3 4
4 0 4 4
4 0 5 4
4 0 6 4
5 0 NULL NULL
Select * from A left join B on A.A_ID=B.A_ID and A.IS_ACTIVE=0
Following output is the actual output of above query with no effect to records by adding AND is_active=0 after ON clause.
A_ID IS_ACTIVE B_ID A_ID
1 1 1 1
1 1 2 1
2 0 NULL NULL
3 1 NULL NULL
4 0 3 4
4 0 4 4
4 0 5 4
4 0 6 4
5 0 NULL NULL
Following output is the required output which i need to solve my problem.
A_ID IS_ACTIVE B_ID A_ID
1 1 NULL NULL
1 1 NULL NULL
2 0 NULL NULL
3 1 NULL NULL
4 0 3 4
4 0 4 4
4 0 5 4
4 0 6 4
5 0 NULL NULL
I am facing problem in getting exact records which are required.
I need all records from Table A and matching records from Table B but
those records of Table B which are equal to is_active=0 of Table A.
Note : Query should show all records of Table A
Please help me how can i get this scenario in Left Join of SQL.
I tried your examples as code. And I get the result you needed. What is the problem?
CREATE TABLE #a(a_id int, is_active bit)
CREATE TABLE #b(b_id int, a_id int)
INSERT INTO #a(a_id,is_active)
VALUES(1,1),(2,0),(3,1),(4,0),(5,0)
INSERT INTO #b(b_id,a_id)
VALUES(1,1),(2,1),(3,4),(4,4),(5,4),(6,4)
SELECT *
FROM #a as a
LEFT JOIN #b as b
ON a.a_id = b.a_id
AND a.is_active = 0
DROP TABLE #a
DROP TABLE #b
Have you tried:
Select * from A left join B on A.A_ID=B.A_ID
Where A.IS_ACTIVE=0
Hi im new with sql queries i need help.
I have a table which have QuestionId from 0 to 8 but sometimes except zero a value can be repeated, what i want is top create a group from 0 to 8 and when other 0 occurs it will be a new group, instead of creating i want to give random number to SurveyID column.
QuestionID SurveyID
0 NULL
1 NULL
2 NULL
3 NULL
4 NULL
4 NULL
5 NULL
6 NULL
7 NULL
7 NULL
8 NULL
8 NULL
0 NULL
1 NULL
2 NULL
3 NULL
4 NULL
4 NULL
5 NULL
6 NULL
6 NULL
7 NULL
8 NULL
0 NULL
1 NULL
2 NULL
i want to update the surveyID in group from first 0 before second 0.
I want to give Unique random value to the group form 0 to 8 then again a new random value to 0 - 8
QuestionID SurveyID
0 8888
1 8888
2 8888
3 8888
4 8888
4 8888
5 8888
6 8888
7 8888
7 8888
8 8888
8 8888
0 1232
1 1232
2 1232
3 1232
I want this to be done.
You can use a CTE query to generate SurveyId the way you want. I assumed that your primary key column is named Id and table seq - please replace that. You can also use a different way to generate random numbers - up to you.
with cte as
(
select Id, questionId, convert(int, rand() * Id * 1000) as surverId
from seq
where questionId = 0
union all
select seq.Id, seq.questionId, cte.surverId
from seq
inner join cte on cte.Id + 1 = seq.Id
where seq.questionId <> 0
)
merge seq as target
using (select * from cte) as source (Id, questionId, surveyId)
on (target.Id = source.Id)
when matched then
update set target.SurveyId = source.surveyId;