SQL , Cross join alternative/faster solution - sql

We have a simple table with two columns like
+-------+-------+
| fname | lname |
+-------+-------+
| foo | bar |
+-------+-------+
we also have another table which contains Months
+-------+
| month |
+-------+
| jan |
| feb |
| mar |
+-------+
The goal is to get a table like:
+-------+-------+-------+
| fname | lname | month |
+-------+-------+-------+
| foo | bar | jan |
| foo | bar | feb |
| foo | bar | mar |
+-------+-------+-------+
To get this table, I used a cross join, but the fetching time has increased exponentially when data are increased. This should be a simple duplication of data, and just the month column should be added at the end of each row, but it seems more complex behind the scenes. Can this query be run faster in any alternative way?

data
CREATE TABLE table1(fname varchar(100),lname varchar(100))
insert into table1
(fname,lname) values
('foo','bar');
CREATE TABLE table2(month varchar(100))
insert into table2
(month) values
('jan'),
('feb'),
('mar');
the alternative way is using Cross Apply but it is slower than Cross Join
SELECT fname,
lname,
month
FROM table2
CROSS apply (SELECT *
FROM table1
WHERE 1 = 1) A
if your first table has one row structure forget cross join and use value and alias
SELECT 'foo' fname,
'bar' lname,
month
FROM table2
or
declare #fname varchar(100)=(select fname from table1)
declare #lname varchar(100)=(select lname from table1)
select #fname fname,
#lname lname,
month
FROM table2
dbfiddle

Related

Is it possible to get list of all id's that are grouped by

WE have a springboot application and there a scenario where we are using native query to insert data into a table based on the groupby query on another table. Now, I need to get the list of all the id's of the records that are grouped by before insert.
Query:
insert into table2 (column1,column2)
select column1,column2
from table1
group by column1,column2
Now, I need to get the list of id's that are grouped in table1. Is it possible?
Since you are using native query, does a grouping at SQL level like below solve your problem? (below is MYSQL code)
select column1,column2, group_concat(id_column)
from table1
group by column1,column2
With only access to table 2 you cannot get the ids what were grouped in table1, unless column1 or column2 is the id itself.
Here is an example where we have a marker in the users table to know whether the id's have been summarised. The function returns the list of id's to summarise, inserts the information into the table summary and marks the id's as inserted.
This does not account for find more users created in a month already summarised (it will create a second row for the same month). It could be modified to update if the month+year is found, but it demonstrates what I understand you are trying to achieve.
create table users (
id int,
created date,
summarised int default 0);
insert into users (id, created)values
(1,'2020-01-01'),
(2,'2020-01-15'),
(3,'2020-02-15');
create table summary(
year int,
month int,
num_id int);
✓
3 rows affected
✓
CREATE FUNCTION Summarise()
RETURNS TABLE (ID int)
LANGUAGE plpgsql AS
$BODY$
BEGIN
RETURN QUERY
SELECT users.id from users where summarised = 0;
insert into summary
select
date_part('year',users.created),
date_part('month',users.created)
,count(users.id)
from users
where users.summarised = 0
group by date_part('year',users.created), date_part('month',users.created);
UPDATE users set summarised = 1;
END;
$BODY$
✓
select * from users;
select * from summary;
select Summarise();
select * from users;
select * from summary;
id | created | summarised
-: | :--------- | ---------:
1 | 2020-01-01 | 0
2 | 2020-01-15 | 0
3 | 2020-02-15 | 0
year | month | num_id
---: | ----: | -----:
| summarise |
| --------: |
| 1 |
| 2 |
| 3 |
id | created | summarised
-: | :--------- | ---------:
1 | 2020-01-01 | 1
2 | 2020-01-15 | 1
3 | 2020-02-15 | 1
year | month | num_id
---: | ----: | -----:
2020 | 1 | 2
2020 | 2 | 1
db<>fiddle here

Insert into table with multiple select including select distinct from two tables

I have two SQL Server tables (simplified) below:
Table1
| productname | certno | expdate | company
+-------------+--------+------------+---------
| abc | 123 | 2020-01-01 | ABC
| def | 123 | 2020-01-01 | ABC
| qwe | 456 | 2020-01-02 | ABC
| asd | 999 | 2020-02-02 | DEF
| .. | .. | .. | ..
Table2
| companyid | company
+-----------+---------
| 1 | ABC
| 2 | DEF
| .. | ..
And I need to insert data from these 2 tables into another table for distinct certno, Table3 should be like this:
| certid | companyid | certno | expdate | createddate | is_null
+--------+-----------+--------+------------+-------------+---------
| 1 | 1 | 123 | 2020-01-01 | 2020-09-12 | 1
| 2 | 1 | 456 | 2020-01-02 | 2020-09-12 | 1
| 3 | 2 | 999 | 2020-02-02 | 2020-09-12 | 1
| .. | .. | .. | .. | .. | ..
I'm currently using this query, I need to create a script so that next batch of update is just in one query.
insert into Table3 (certno, expdate, is_null)
select distinct certno, expdate
from Table1
where company like '%ABC%';
--then I will update Table3 to complete it
update Table3
set companyid = '1',
createddate = getdate(),
is_null = '1'
where is_null is null; --edited**
--then repeat the above until I have all companies registered as companyid in Table3
But this will be a hassle when it involved more companies and lots of data and I was thinking of using multiple select including select distinct but I'm stuck right here:
insert into Table3 (companyid, certno, expdate, createddate)
select
(select companyid from Table2
where company in (select company from Table1)
),
(select distinct certno),
expdate,
getdate()
from Table1;
I did not run the script to see whether it works or not because I don't want to mess up the Table3 data and I feel that the script is not going to work.
Is there any way to just run the query in one script?
I think you want insert . . . select with join:
insert into Table3 (companyid, certno, expdate, createddate, is_null)
select distinct t2.companyid, t1.certno, t1.expdate, getdate(), 1
from Table1 t1 join
Table2 t2
on t1.company = t2.company
where t1.company like '%ABC%';
You can remove the where clause to insert all companies in one insert.
Looks like you just need to expand the columns in the SELECT:
INSERT INTO dbo.Table3 (certno, expdate, companyid, createdate)
SELECT DISTINCT
certno,
expdate,
company,
GETDATE()
FROM dbo.Table1;

Count redundant for each value in a column in SQL server

If I have the following table in a SQL Server 2019 database as follows:
|id | name | count |
+-----+--------+--------+
| 1 | rose | 1 |
| 2 | peter | 1 |
| 3 | ann | 1 |
| 4 | rose | 2 |
| 5 | ann | 2 |
| 6 | ann | 3 |
| 7 | mike | 1 |
I would like to find out if an inserted name already exists in the column "name" and how many times and right a count next to it as shown in "count" column. For example when ann was inserted the second time I put count value bext to it which is 2, and ann was inserted the third time I put 3 next to it.
How to do that using SQL?
Thank you
Here is one approach using the insert ... select syntax:
insert into mytable (name, cnt)
select v.name, (select count(*) + 1 from mytable t where t.name = v.name)
from (values (#name)) v(name)
The value to insert is given as #name in derived table values(). Then we use a subquery to count how many such names already exist in the table.
I would suggest that you calculate this information when you query the table, rather than when you insert rows:
select t.*,
row_number() over (partition by name order by id) as count
from t;
The value will always be correct -- even when the data is updated or rows are deleted.
Select Name, Count(*)
From Table
Group By Name

Join on different tables depending on column associated with value being joined on?

I am trying to create a view that provides different tables depending on a column associated with the value being joined on.
Example:
SomeTable
+-----+------+
| ID | Type |
+-----+------+
| 123 | 1 |
| 124 | 2 |
| 125 | 1 |
| 126 | 2 |
+-----+------+
TableA
+---------+---------------+-----+------------+
| Item_ID | Serial_Number | ID | LocationID |
+---------+---------------+-----+------------+
| 1 | 19-001 | 124 | 4 |
| 2 | 19-002 | 126 | 17 |
+---------+---------------+-----+------------+
TableB
+-----+------------+----------+
| ID | LocationID | Quantity |
+-----+------------+----------+
| 123 | 7 | 15 |
| 125 | 12 | 10 |
+-----+------------+----------+
SELECT t.ID, v.LocationID
FROM SomeTable t
FULL JOIN NewView v ON t.ID = v.ID
WHERE t.ID = 123
If the ID the view is being joined on is Type 1 then the View selects Table A. If the ID is Type 2, then select Table B.
The expected result for ID 123 would be:
+-----+------------+
| ID | LocationID |
+-----+------------+
| 123 | 7 |
+-----+------------+
And the expected result for ID 124 would be:
+-----+------------+
| ID | LocationID |
+-----+------------+
| 124 | 4 |
+-----+------------+
I know I could do this by using a function and passing a parameter like this:
SELECT t.ID, f.LocationID
FROM SomeTable t
FULL JOIN NewFunction(123) f ON t.ID = f.ID
WHERE t.ID = 123
And here is the function:
CREATE FUNCTION NewFunction (#ID)
RETURNS #ReturnTable TABLE (ID INT, LocationID INT)
BEGIN
DECLARE #Type INT
SET #Type = (SELECT Type FROM SomeTable WHERE ID = #ID)
IF #Type = 1
INSERT INTO #ReturnTable (ID, LocationID)
SELECT t.ID, a.LocationID
FROM SomeTable t
FULL JOIN TableA a
WHERE t.ID = #ID
ELSE
INSERT INTO #ReturnTable (ID, LocationID)
SELECT t.ID, b.LocationID
FROM SomeTable t
FULL JOIN TableB b
WHERE t.ID = #ID
RETURN
END
The problem is that passing a parameter to a function like this will require making changes to the application. I would prefer not to have to implement these changes, so it would be ideal if I could recreate the functionality in the example above either with a view or a function that does not require a parameter. Any ideas?
Why not create a view for all three tables?
select t.id, coalesce(a.locationid, b.locationid) as locationid
from sometable t left join
a
on t.id = a.id and t.type = 1 left join
b
on t.id = b.id and t.type = 2;
You need the type to get the right table, so it seems that all three tables should be used together.
You can't send a parameter into a VIEW, so it isn't possible to make it do different things depending on the content of the select query.
What you could do, is to use a UNION query assuming that Table A and Table B have the same columns, then use the Type column in your WHERE clause

Find unique column value from records where another column has ALL of the values from a set

Let's say I have a table like this:
+----+-------+
| ID | Word |
+----+-------+
| 1 | a |
| 1 | dog |
| 1 | has |
| 2 | two |
| 2 | three |
| 2 | four |
| 2 | five |
| 3 | black |
| 3 | red |
+----+-------+
I want to find the unique ID value where there are records with that ID that have all of the Word values in a provided set. E.g. WHERE Word IN ('a', 'dog', 'has') would return ID value 1 but WHERE Word IN ('a', 'dog', 'has', 'black') would return NULL.
Is this possible?
Use group by and having:
select id
from t
where word in ('a', 'dog', 'has')
group by id
having count(*) = 3; - the number of words in the list
If you're using SQL 2016 or higher this seems like a good solution:
--Load test data
DECLARE #tbl as TABLE (id int, word varchar(40))
INSERT INTO #tbl
(id, word)
VALUES
(1, 'a'),(1,'dog'),(1,'cat'),(1,'has'),(1,'a'),(2,'two'),(2,'three'),(2,'four'),(2,'five'),(3,'black'),(3,'red')
--ACTUAL SOLUTION STARTS HERE
DECLARE #srch as VARCHAR(200)
SET #srch = 'a,dog,has'
SELECT id
FROM #tbl
WHERE word IN (SELECT value FROM STRING_SPLIT(#srch,','))
GROUP BY id
HAVING COUNT(DISTINCT word) = (SELECT COUNT(DISTINCT value) FROM STRING_SPLIT(#srch,','));