SQL query check value exists in lookup based on another column value - sql

I have Name/Value pair records in a table and I need to confirm the values exist against a lookup for each Name
KeyVal - Table of NameValue pairs
| MyID1 | MyRecNumber | MyFieldName | MyFieldValue |
|-------|-------------|-------------|--------------|
| 1 | 1 | FirstField | One |
| 2 | 1 | SecondField | Car |
| 3 | 2 | FirstField | Two |
| 4 | 2 | SecondField | Firetruck |
| 5 | 3 | FirstField | Blue |
| 6 | 3 | SecondField | Car |
LookupTable - Table to match Name Values (from KeyVal) with LookupValue (in CheckVals table)
| MyID2 | MyFieldName | LookupName |
|-------|-------------|------------|
| 1 | FirstField | FieldOne |
| 2 | SecondField | FieldTwo |
CheckVals - Table with valid values for each field
| MyID3 | LookupFieldName | LookupValue |
|-------|-----------------|-------------|
| 1 | FieldOne | One |
| 2 | FieldOne | Two |
| 3 | FieldOne | Three |
| 4 | FieldTwo | Car |
| 5 | FieldTwo | Truck |
| 6 | FieldTwo | Bus |
I have a query that will check values against a single name lookup, but am unsure how to make this check all names against the lookup table. In this query it bypasses the LookupTable as I specify the lookup value in the query itself.
DECLARE #AttributeName AS VARCHAR(50)
DECLARE #Lookup AS VARCHAR(50)
SET #AttributeName = 'SecondField'
SET #Lookup = 'FieldTwo';
SELECT
MyRecNumber,
MyFieldName,
MyFieldValue
FROM
dbo.KeyVal kv
WHERE
MyFieldName = #AttributeName
AND MyFieldValue NOT IN
(
SELECT
LookupValue
FROM
dbo.CheckVals cv
WHERE cv.LookupFieldName = #Lookup
)
Question: How can I do a lookup against all values in the KeyVal table, through the LookupTable table, to check if the value in MyFieldValue exists in CheckVals against the MyFieldName and LookupName match?
This is what I'm hoping to get - the two rows that have invalid values are returned in the query results
| MyRecNumber | MyFieldName | MyFieldValue |
|-------------|-------------|--------------|
| 2 | SecondField | Firetruck |
| 3 | FirstField | Blue |
Sample Tables
CREATE TABLE [dbo].[KeyVal](
[MyID1] [smallint] IDENTITY(1,1) NOT NULL,
[MyRecNumber] [smallint] NULL,
[MyFieldName] [varchar](50) NULL,
[MyFieldValue] [varchar](50) NULL
) ON [PRIMARY]
CREATE TABLE [dbo].[LookupTable](
[MyID2] [smallint] IDENTITY(1,1) NOT NULL,
[MyFieldName] [varchar](50) NULL,
[LookupName] [varchar](50) NULL
) ON [PRIMARY]
CREATE TABLE [dbo].[CheckVals](
[MyID3] [smallint] IDENTITY(1,1) NOT NULL,
[LookupFieldName] [varchar](50) NULL,
[LookupValue] [varchar](50) NULL
) ON [PRIMARY]
Sample Data
INSERT INTO [dbo].[KeyVal]
([MyRecNumber], [MyFieldName], [MyFieldValue])
VALUES
(1, 'FirstField', 'One'),
(1, 'SecondField', 'Car'),
(2, 'FirstField', 'Two'),
(2, 'SecondField', 'Firetruck'),
(3, 'FirstField', 'Blue'),
(3, 'SecondField', 'Car')
INSERT INTO [dbo].[LookupTable]
([MyFieldName], [LookupName])
VALUES
('FirstField', 'FieldOne'),
('SecondField', 'FieldTwo')
INSERT INTO [dbo].[CheckVals]
([LookupFieldName], [LookupValue])
VALUES
('FieldOne', 'One'),
('FieldOne', 'Two'),
('FieldOne', 'Three'),
('FieldTwo', 'Car'),
('FieldTwo', 'Truck'),
('FieldTwo', 'Bus')

Let me assume that you want the rows in the first table where the values do not match:
select kv.*
from keyval kv left join
lookuptable lt
on kv.myfieldname = lt.myfieldname left join
checkvals cv
on cv.LookupFieldName = lt.LookupName and
cv.LookupValue = kv.MyFieldValue
where cv.myid3 is null;

Related

Convert values in related table to comma-separated list

I have two SQL Server tables:
TableA TableB
+------+--------+ +-----+------------+
| aid | Name | | aid | Activity |
+------+--------+ +-----+------------+
| 1 | Jim | | 1 | Skiing |
| 2 | Jon | | 1 | Surfing |
| 3 | Stu | | 1 | Riding |
| 4 | Sam | | 3 | Biking |
| 5 | Kat | | 3 | Flying |
+------+--------+ +-----+------------+
I'm trying to the following result where the related activities are in a comma-separated list:
+------+--------+------------------------------+
| aid | Name | Activity |
+------+--------+------------------------------+
| 1 | Jim | Skiing, Surfing, Riding |
| 2 | Jon | NULL |
| 3 | Stu | Biking, Flying |
| 4 | Sam | NULL |
| 5 | Kat | NULL |
+------+--------+------------------------------+
I tried:
SELECT aid, Name, STRING_AGG([Activity], ',') AS Activity
FROM TableA
INNER JOIN TableB
ON TableA.aid = TableB.aid
GROUP BY aid, Name
Can someone help me with this SQL query? Thank you.
You could use OUTER APPLY to aggregate the string if you're using SQL Server 2017 or higher.
drop table if exists #TableA;
go
create table #TableA (
aid int not null,
[Name] varchar(10) not null);
insert #TableA(aid, [Name]) values
(1, 'Jim'),
(2, 'Jon'),
(3, 'Stu'),
(4, 'Sam'),
(5, 'Kat');
drop table if exists #TableB;
go
create table #TableB (
aid int not null,
[Activity] varchar(10) not null);
insert #TableB(aid, [Activity]) values
(1, 'Skiing'),
(1, 'Surfing'),
(1, 'Riding'),
(3, 'Biking'),
(3, 'Flying');
select a.aid, a.[Name], oa.sa
from #TableA a
outer apply (select string_agg(b.Activity, ', ') sa
from #TableB b
where a.aid=b.aid) oa;
Name sa
Jim Skiing, Surfing, Riding
Jon NULL
Stu Biking, Flying
Sam NULL
Kat NULL

pivot then group on value

I have a logs table with the following definition:
Column | Type | Collation | Nullable | Default
------------------+-----------------------+-----------+----------+---------
id | integer | | not null |
work_location_id | uuid | | not null |
hard_disk_id | integer | | not null |
and a works table with the following definition:
Column | Type | Collation | Nullable | Default
-------------+-----------------------+-----------+----------+---------
id | integer | | not null |
location_id | uuid | | not null |
f_index | integer | | not null |
f_name | character varying(40) | | not null |
f_value | character varying(40) | | not null |
The logs table has data such as:
id | work_location_id | hard_disk_id
----+--------------------------------------+--------------
1 | 40e6215d-b5c6-4896-987c-f30f3678f608 | 1
2 | 3f333df6-90a4-4fda-8dd3-9485d27cee36 | 2
3 | c17bed94-3a9c-4c21-be49-dc77f96d49dc | 3
4 | 6ecd8c99-4036-403d-bf84-cf8400f67836 | 4
5 | 6ecd8c99-4036-403d-bf84-cf8400f67836 | 5
And the works table has data such as:
id | location_id | f_index | f_name | f_value
----+--------------------------------------+---------+-------------+------------
1 | 40e6215d-b5c6-4896-987c-f30f3678f608 | 1 | plot_crop | pears
2 | 3f333df6-90a4-4fda-8dd3-9485d27cee36 | 1 | plot_crop | pears
3 | c17bed94-3a9c-4c21-be49-dc77f96d49dc | 1 | plot_crop | pears
4 | 1cdc7c05-0acd-46cb-b48a-4d3e240a4548 | 1 | plot_crop | pears
5 | dae1eee7-508f-4a76-8906-8ff7b8bfab26 | 1 | plot_crop | pears
6 | 6ecd8c99-4036-403d-bf84-cf8400f67836 | 1 | plot_id | 137
7 | 6ecd8c99-4036-403d-bf84-cf8400f67836 | 2 | farmer_name | John Smith
Desired Output
I want to be able to query the two tables and get the following output
location_id | plot_id | farmer_name
---------------------------------------+---------+-------------
40e6215d-b5c6-4896-987c-f30f3678f608 | None | None
3f333df6-90a4-4fda-8dd3-9485d27cee36 | None | None
c17bed94-3a9c-4c21-be49-dc77f96d49dc | None | None
6ecd8c99-4036-403d-bf84-cf8400f67836 | 137 | John Smith
Notice how for location_id = 6ecd8c99-4036-403d-bf84-cf8400f67836, both values are now showing in one row. I tried to use group by location_id but that didn't work, I was still getting duplicates.
I have also created a db-fiddle.
This looks like conditional aggregation:
select location_id,
max(f_value) filter (where f_name = 'plot_id') as plot_id,
max(f_value) filter (where f_name = 'farmer_name') as farmer_name
from t
group by location_id;
In other databases, you would just use:
max(case when f_name = 'plot_id' then f_value end) as plot_id
As you want to have None as text
Schema (PostgreSQL v13)
-- create table
create table logs (
id integer not null,
work_location_id uuid not null,
hard_disk_id integer not null
);
create table works (
id integer not null,
location_id uuid not null,
f_index integer not null,
f_name varchar(40) not null,
f_value varchar(40) not null
);
-- insert data into table
insert into logs (id, work_location_id, hard_disk_id) values
(1, '40e6215d-b5c6-4896-987c-f30f3678f608', 1),
(2, '3f333df6-90a4-4fda-8dd3-9485d27cee36', 2),
(3, 'c17bed94-3a9c-4c21-be49-dc77f96d49dc', 3),
(4, '6ecd8c99-4036-403d-bf84-cf8400f67836', 4),
(5, '6ecd8c99-4036-403d-bf84-cf8400f67836', 5);
insert into works (id, location_id, f_index, f_name, f_value) values
(1, '40e6215d-b5c6-4896-987c-f30f3678f608', 1, 'plot_crop', 'pears'),
(2, '3f333df6-90a4-4fda-8dd3-9485d27cee36', 1, 'plot_crop', 'pears'),
(3, 'c17bed94-3a9c-4c21-be49-dc77f96d49dc', 1, 'plot_crop', 'pears'),
(4, '1cdc7c05-0acd-46cb-b48a-4d3e240a4548', 1, 'plot_crop', 'pears'),
(5, 'dae1eee7-508f-4a76-8906-8ff7b8bfab26', 1, 'plot_crop', 'pears'),
(6, '6ecd8c99-4036-403d-bf84-cf8400f67836', 1, 'plot_id', '137'),
(7, '6ecd8c99-4036-403d-bf84-cf8400f67836', 2, 'farmer_name', 'John Smith');
Query #1
select w.location_id,
COALESCE(MAX(case
when w.f_name = 'plot_id' then w.f_value
else NULL
end),'None') as "plot_id",
COALESCE(MAX(case
when w.f_name = 'farmer_name' then w.f_value
else NULL
end),'None') as "farmer_name"
from logs l
inner join works w on w.location_id = l.work_location_id
GROUP BY location_id;
location_id
plot_id
farmer_name
3f333df6-90a4-4fda-8dd3-9485d27cee36
None
None
40e6215d-b5c6-4896-987c-f30f3678f608
None
None
6ecd8c99-4036-403d-bf84-cf8400f67836
137
John Smith
c17bed94-3a9c-4c21-be49-dc77f96d49dc
None
None
View on DB Fiddle

Bring information from 2 tables and put it in the same column

I have this table of articles
---------------------------------------------
| Article | Location | existence |
---------------------------------------------
| 200116 | cc3111 | 1 |
---------------------------------------------
| 200116 | ee3091 | 1 |
---------------------------------------------
And this count table
----------------------------------------------------
| Article | Location | Quantity |
----------------------------------------------------
| 200116 | cc3111 | 10 |
----------------------------------------------------
| 200116 | EE3091 | 8 |
----------------------------------------------------
| 200116 | EE2102 | 5 |
----------------------------------------------------
| 200116 | DD5131 | 7 |
----------------------------------------------------
What I'm trying to do is give me one result like the next.
--------------------------------------------------------------------------
| Article | Location | Existence | Quantity | DIF |
--------------------------------------------------------------------------
| 200116 | CC3111 | 1 | 10 | 9 |
--------------------------------------------------------------------------
| 200116 | EE3091 | 1 | 8 | 7 |
--------------------------------------------------------------------------
| 200116 | DD5131 | 0 | 7 | 7 |
--------------------------------------------------------------------------
| 200116 | EE2102 | 0 | 5 | 5 |
----------------------------- -------------------------------------------
But without doing it with a Union since then I have to make another query, I just do not know how to join it without usingUnion.
-SOME HELP TO PERFORM THIS QUERY
-I do not know how to add the location in the same column
This looks like a left join:
select t2.*, coalesce(t1.existence, 0) as existence,
(t2.quantity - coalesce(t1.existence, 0)) as dif
from t2 left join
t1
on t2.article = t1.article and t2.location = t1.location;
Here's another option...
IF OBJECT_ID('tempdb..#Article', 'U') IS NOT NULL
DROP TABLE #Article;
CREATE TABLE #Article (
Article INT NOT NULL,
Location CHAR(6) NOT NULL,
Existance INT NOT NULL
);
IF OBJECT_ID('tempdb..#Counts', 'U') IS NOT NULL
DROP TABLE #Counts;
CREATE TABLE #Counts (
Article INT NOT NULL,
Location CHAR(6) NOT NULL,
Quantity INT NOT NULL
);
INSERT #Article(Article, Location, Existance) VALUES
(200116, 'cc3111', 1), (200116, 'ee3091', 1);
INSERT #Counts(Article, Location, Quantity) VALUES
(200116, 'cc3111', 10), (200116, 'EE3091', 8),
(200116, 'EE2102', 5), (200116, 'DD5131', 10);
SELECT * FROM #Article a;
SELECT * FROM #Counts c;
--=====================================================
SELECT
Article = COALESCE(a.Article, c.Article),
Location = COALESCE(a.Location, c.Location),
Existance = COALESCE(a.Existance, 0),
Quantity = COALESCE(c.Quantity, 0),
DIF = COALESCE(c.Quantity, 0) - COALESCE(a.Existance, 0)
FROM
#Article a
FULL JOIN #Counts c
ON a.Article = c.Article
AND a.Location = c.Location;
You need to JOIN these tables instead of doing a UNION. The former will put columns side by side, whereas the latter will stack two similar sources together...

SQL Server - List of parameters into function, Export Multiple results into Excel

I have a function that takes an ID as an input. The function gets all related IDs from the ID you input. When I run the function five times with different ID numbers such as:
select * from [levelfunction](100)
select * from [levelfunction](101)
select * from [levelfunction](102)
select * from [levelfunction](104)
select * from [levelfunction](108)
CREATE TABLE #Table1(
ID [int] NULL,
Level [int] NULL)
INSERT INTO #Table1 (ID, Level) VALUES (100,0)
CREATE TABLE #Table2(
ID [int] NULL,
Level [int] NULL)
INSERT INTO #Table2 (ID, Level) VALUES (101,0)
CREATE TABLE #Table3(
ID [int] NULL,
Level [int] NULL)
INSERT INTO #Table3 (ID, Level) VALUES (102,0)
CREATE TABLE #Table4(
ID [int] NULL,
Level [int] NULL)
INSERT INTO #Table4 (ID, Level) VALUES (103,0), (104,1), (105,2), (106,3)
CREATE TABLE #Table5(
ID [int] NULL,
Level [int] NULL)
INSERT INTO #Table5 (ID, Level) VALUES (107,0), (108,1), (109,2)
I get the below result:
select * from #Table1
select * from #Table2
select * from #Table3
select * from #Table4
select * from #Table5
+-----+-------+
| ID | Level |
+-----+-------+
| 100 | 0 |
+-----+-------+
+-----+-------+
| ID | Level |
+-----+-------+
| 101 | 0 |
+-----+-------+
+-----+-------+
| ID | Level |
+-----+-------+
| 102 | 0 |
+-----+-------+
+-----+-------+
| ID | Level |
+-----+-------+
| 103 | 0 |
| 104 | 1 |
| 105 | 2 |
| 106 | 3 |
+-----+-------+
+-----+-------+
| ID | Level |
+-----+-------+
| 107 | 0 |
| 108 | 1 |
| 109 | 2 |
+-----+-------+
How do I -
-Put a list of IDs into a function without altering the function?
-Export all the tables into excel as 1 result?
Expected excel result:
+-----+-------+
| ID | Level |
+-----+-------+
| 100 | 0 |
| 101 | 0 |
| 102 | 0 |
| 103 | 0 |
| 104 | 1 |
| 105 | 2 |
| 106 | 3 |
| 107 | 0 |
| 108 | 1 |
| 109 | 2 |
+-----+-------+
For
Put a list of IDs into a function without altering the function?
use CROSS APPLY
declare #IDlist table(ID int)
insert into #IDlist(ID) values (100),(101),(102),(103),(104),(105),(106),(107)
select a.Id,a.Level from #IDlist d
cross apply levelfunction(d.ID) as a;
About export to excel was answered more then one time, for example here
OLE DB provider "Microsoft.ACE.OLEDB.12.0" for linked server "(null)" returned message "Bookmark is invalid."

MySQL 2 level MENU Query

I'm trying to do a MySQL request to retreive a 2 level menu (parent and children)...
There's only 1 table, of course :
idCategory | Title | idCategoryParent | DisplayOrder
1 | cat1 | NULL | 1
2 | sub-cat1 | 1 | 1
3 | sub-cat2 | 1 | 2
4 | cat2 | NULL | 2
5 | sub-cat3 | 4 | 1
6 | sub-cat4 | 4 | 2
7 | cat3 | NULL | 3
I'm looking for those results :
titleCat | titleSubCat | idCategory
cat1 | sub-cat1 | 1
cat1 | sub-cat2 | 1
cat2 | sub-cat3 | 4
cat2 | sub-cat4 | 4
cat3 | NULL | 7
OR something like that would be fine too :
cat1 | null | 1
cat1 | sub-cat1 | 1
cat1 | sub-cat2 | 1
etc..
I tried with something like :
SELECT subcat.title as catTitle, cat.title as parentTitle, subcat.idCategory as catIdCategory, subcat.idCategoryParent
FROM `test_category` as cat
RIGHT OUTER JOIN test_category as subcat ON cat.idCategory=subcat.idCategoryParent
Doesn't work bad but I struggle trying to order the records...
Here's the SQL Dump if you want to try it :
--
-- Table structure for table `test_category`
--
CREATE TABLE IF NOT EXISTS `test_category` (
`idCategory` int(11) NOT NULL AUTO_INCREMENT,
`idCategoryParent` int(11) DEFAULT NULL,
`title` varchar(20) NOT NULL,
`order` int(11) NOT NULL,
PRIMARY KEY (`idCategory`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=8 ;
--
-- Dumping data for table `test_category`
--
INSERT INTO `test_category` (`idCategory`, `idCategoryParent`, `title`, `order`) VALUES
(1, NULL, 'cat1', 1),
(2, 1, 'sub-cat1', 1),
(3, 1, 'sub-cat2', 2),
(4, NULL, 'cat2', 2),
(5, 4, 'sub-cat3', 1),
(6, 4, 'sub-cat4', 2),
(7, NULL, 'cat3', 3);
Thanks! :)
Your query is almost correct, but you need to use LEFT JOIN if you want categories with no subcategories, and you should select only first-level categories from the first table.
SELECT t1.title, t2.title, t1.idCategory
FROM
test_category t1
LEFT JOIN test_category t2 ON t2.idCategoryParent=t1.idCategory
WHERE t1.idCategoryParent IS NULL
ORDER BY t1.DisplayOrder, t2.DisplayOrder