Is it possible to multiply the results of two select statements? - sql

If I have a select statement like this...
SELECT col1 FROM table WHERE col2=1
Which returns the results 1,2,3,4,5.
And another select statement like this...
SELECT col1 FROM table WHERE col2=2
Which return the results 6,7,8,9,10.
Is there a way to multiply the results of these two queries, so that a final result set of 6,14,24,36,50 is returned?
I've tried simple things like this.
SELECT (SELECT col1 FROM table WHERE col2=1) * (SELECT col1 FROM table WHERE col2=2)
But that didn't seem to work.
If it makes a difference, I'm using Sqlite, and the value types are REAL not INTEGER.

DECLARE #Table1 TABLE ( thing real)
DECLARE #Table2 TABLE (anotherThing real)
INSERT #Table1 (thing)
VALUES (1), (2), (3), (4), (5)
INSERT #Table2 (anotherThing)
VALUES (6), (7), (8), (9), (10)
;
SELECT T.thing * TT.anotherThing AS Multi
FROM (
SELECT T.thing
, (
SELECT COUNT(1) RN
FROM #Table1 AS T2
WHERE T2.thing <= T.Thing
) RN
FROM #Table1 AS T
)AS T
INNER JOIN (
SELECT TT.anotherThing
, (
SELECT COUNT(1) RN
FROM #Table2 AS T2
WHERE T2.anotherThing <= TT.anotherThing
) RN
FROM #Table2 AS TT
) TT
ON TT.RN = T.RN
Result:
+-------+
| Multi |
+-------+
| 6 |
| 14 |
| 24 |
| 36 |
| 50 |
+-------+
The example probably won't work in sqlite, I wrote it in sql server. This shows it works on sqlite.

Related

Need to return an ID which has start and END in sql server

I have a scenario wherein I need to find the ID which only has start and END in it. Below is the table for reference.
Declare #T Table ( ID int, Name varchar(100))
Insert into #T values (1,'Start')
Insert into #T values (1,'END')
Insert into #T values (1,'Stuart')
Insert into #T values (1,'robin')
Insert into #T values (2,'Start')
Insert into #T values (2,'END')
Insert into #T values (3,'Start')
Insert into #T values (4,'END')
I want the Output as:
ID Name
2 Start
2 END
I want those ID which only has start and end in it.
What I tried so far:
SELECT * FROM #T t
WHERE EXISTS (SELECT * FROM #T WHERE id = t.id AND name = 'start')
AND EXISTS (SELECT * FROM #T WHERE id = t.id AND name = 'END')
But my query is giving ID 1 as well.
Can someone please help me rectify the problem.
I presume your issue is that record 1 has a 'Stuart' in it too?
As such, you can do a similar check in the WHERE e.g.,
SELECT * FROM #T t
WHERE EXISTS (SELECT * FROM #T WHERE id = t.id AND name = 'start')
AND EXISTS (SELECT * FROM #T WHERE id = t.id AND name = 'END')
AND NOT EXISTS (SELECT * FROM #T WHERE id = t.id AND name NOT IN ('start','END'))
Note that you may want to consider
What happens if you have two 'start' rows or two 'end' rows (e.g., start-start-end)? Can you even have two 'start' rows (e.g., start-start)?
What happens if you have a blank/NULL (e.g., start-NULL-end)?
EDIT: removed 'What happens if they're out of order (e.g., end-start)?' as a question as there is no sorting in the data at all (e.g., not even an implicit sort).
You can go for CTE to get group wise count and total count as 2.
Declare #T Table ( ID int, Name varchar(100))
Insert into #T values (1,'Start')
Insert into #T values (1,'END')
Insert into #T values (1,'Stuart')
Insert into #T values (1,'robin')
Insert into #T values (2,'Start')
Insert into #T values (2,'END')
Insert into #T values (3,'Start')
Insert into #T values (4,'END')
;WITH CTE_Total_StartEnd AS
(
select id, count(*) AS Total_Cnt
, COUNT( case when Name IN ('Start') THEN 1 END) as start_cnt
, COUNT( case when Name IN ('End') THEN 1 END) as end_cnt
from #t
group by id
having COUNT( case when Name IN ('Start') THEN 1 END) =1 and
COUNT( case when Name IN ('End') THEN 1 END) = 1 and
count(*) = 2
)
SELECT t.* from #t t
inner join CTE_Total_StartEnd as c
ON c.id = t.id
+----+-------+
| ID | Name |
+----+-------+
| 2 | Start |
| 2 | END |
+----+-------+
You can do this by using group by function also like below
WITH cte AS
(
SELECT 1 AS id , 'Start' AS name
UNION ALL
SELECT 1 AS id ,'END' AS name
UNION ALL
SELECT 1 AS id ,'Stuart' AS name
UNION ALL
SELECT 1 AS id ,'robin' AS name
UNION ALL
SELECT 2 AS id ,'Start' AS name
UNION ALL
SELECT 2 AS id ,'END' AS name
UNION ALL
SELECT 3 AS id ,'Start' AS name
UNION ALL
SELECT 4 AS id ,'END' AS name
)
SELECT T.ID,SUM(T.VAL)AS SUM
FROM
(
SELECT id,name , CASE WHEN name='Start' THEN 1
WHEN name='END' THEN 2
ELSE 3
END AS VAL
FROM cte
)T
GROUP BY T.ID
HAVING SUM(T.VAL) =3
could you please try this? Pls note i added collate command in the end of sql.
SQL Server check case-sensitivity?
SELECT * FROM #T t
WHERE EXISTS (SELECT * FROM #T WHERE id = t.id AND name = 'start' COLLATE SQL_Latin1_General_CP1_CS_AS)
AND EXISTS (SELECT * FROM #T WHERE id = t.id AND name = 'END' COLLATE SQL_Latin1_General_CP1_CS_AS)

SQL filter out rows with duplicate cell value

I've got a big query that's returning rows where one of the colums has duplicated values, for example:
| sysid | col1 | col2
| A | 1 | 2
| A | 2 | 3
| B | 1 | 4
sysid is the column I want to filter by, so in the above example I only want the last row of output because A appears more than once. I tried to do this to filter them out:
CREATE TABLE #temp (SysId VARCHAR(10), col1 INT);
INSERT INTO #temp VALUES ('A', 1), ('B', 1), ('A', 1);
WITH cte AS (
SELECT * FROM #temp
), sysids AS (
SELECT SysId FROM #temp GROUP BY Sysid HAVING COUNT(*) = 1
)
SELECT * FROM #temp WHERE EXISTS (SELECT SysId FROM sysids);
DROP TABLE #temp;
I expected that final select would only contain the B row, in the above example . I'm still getting all of them though, and I don't understand why.
Obviously in this specific example I don't need the cte part but my real query is pretty complex with multiple unions.
You can use a correlated, aggregating subyquery and NOT EXISTS.
SELECT t1.*
FROM #temp t1
WHERE NOT EXISTS (SELECT ''
FROM #temp t2
WHERE t2.sysid = t1.sysid
HAVING count(*) > 1);
Please try this:
;WITH cte AS (
SELECT t.SysId,t.col1,COUNT(1)OVER(PARTITION BY t.SysID) AS [cnt]
FROM #temp t
)
SELECT c.SysId,c.col1
FROM cte c WHERE c.cnt = 1;
CREATE TABLE #temp (SysId VARCHAR(10), col1 INT);
INSERT INTO #temp VALUES ('A', 1), ('B', 1), ('A', 1);
WITH cte AS (
SELECT * FROM #temp
), sysids AS (
SELECT SysId FROM #temp GROUP BY Sysid HAVING COUNT(SysId) =1
)
SELECT * FROM #temp WHERE SysId IN (SELECT SysId FROM sysids);
DROP TABLE #temp;

Select rows from two tables only whenn 'all in'

I've got the following tables
Table1
Col1 | Col2
r1c1 | r1c2
r2c1 | r2c2
r3c1 | r3c2
r4c1 | r4c2
Table2
Col1_Table1 | Col2
r1c1_table1 | r1c2
r2c1_table1 | r2c2
r3c1_table1 | r3c2
So you see my row #4 is missing in Table2.
Select all rows from table 1 and join table 2. No Problem
But what looks the select like when I want to select all from Table1 but only if all from Table1 are in Table2? I hope you can understand.
In my example I the result of the select has to be zero/null.
If I understand you correctly, you want to return all rows of table 1 only if all of table 1 also exists in table 2
So in effect:
SELECT * FROM #table1
WHERE
(SELECT COUNT(*) FROM #table1)
= (SELECT COUNT(*) FROM #Table1 INNER JOIN #Table2 ON #Table1.col1 = #Table2.col1)
Is that correct? in which case a better way of doing this would be:
SELECT *
FROM #table1
WHERE NOT EXISTS (
SELECT 1
FROM #table1
WHERE col1 NOT IN (SELECT col1 FROM #table2)
);
Hi Please see example below
IF OBJECT_ID('tempdb..#Table1') IS NOT NULL
BEGIN
DROP TABLE #Table1
END
IF OBJECT_ID('tempdb..#Table2') IS NOT NULL
BEGIN
DROP TABLE #Table2
END
CREATE TABLE #Table1 (
Col1 NVARCHAR(20)
, Col2 NVARCHAR(20)
)
INSERT INTO #Table1 VALUES ('r1c1', 'r1c2')
INSERT INTO #Table1 VALUES ('r2c1', 'r2c2')
INSERT INTO #Table1 VALUES ('r3c1', 'r3c2')
INSERT INTO #Table1 VALUES ('r4c1', 'r4c2')
CREATE TABLE #Table2 (
Col1_Table1 NVARCHAR(20)
, Col2 NVARCHAR(20)
)
INSERT INTO #Table2 VALUES ('r1c1_table1','r1c2')
INSERT INTO #Table2 VALUES ('r2c1_table1','r2c2')
INSERT INTO #Table2 VALUES ('r3c1_table1','r3c2')
SELECT COUNT(*) as 'Result'
FROM #Table1 AS t1
INNER JOIN #Table2 AS t2
ON t1.Col1 = t2.Col2
IF OBJECT_ID('tempdb..#Table1') IS NOT NULL
BEGIN
DROP TABLE #Table1
END
IF OBJECT_ID('tempdb..#Table2') IS NOT NULL
BEGIN
DROP TABLE #Table2
END
Hope it helps
I think you are looking for INTERSECT.
Ex:
select Col1, Col2 from table1
INTERSECT
select Col1, Col2 from table2
Intersect will return results based on data being in BOTH tables.
Reference: https://learn.microsoft.com/en-us/sql/t-sql/language-elements/set-operators-except-and-intersect-transact-sql?view=sql-server-2017

How do I replace strings of a table from another table column

How do I update/replace the value of the first table from the list of my second table in SQL. Sorry im not so good in using replace() of SQL especially replacing from values base from different table
First table.
ID | Value
======================
1 | Fruits[Apple]
2 | Fruits[Apple,Mango]
3 | Apple[Red,Green]
Second table
Search | Replace
=========================
Apple | Orange
Green | Yellow
You will need some kind of recursive replace.
something like a loop
declare #t1 table (ID int, Value varchar(max))
declare #t2 table (Search varchar(max), ReplaceWith varchar(max))
insert #t1 values (1, 'Fruits[Apple]'),(2, 'Fruits[Apple,Mango]'), (3, 'Apple[Red,Green]')
insert #t2 values ('Apple', 'Orange'),('Green', 'Yellow')
--loop nth times for rows that have more than one match
while exists(select top 1 * from #t1 inner join #t2 on charindex(Search, Value ) > 0)
begin
update #t1
set Value = replace(Value, Search, ReplaceWith)
from #t2
inner join #t1 on charindex(Search, Value ) > 0
end
select * from #t1
results
ID Value
----- -----------------------
1 Fruits[Orange]
2 Fruits[Orange,Mango]
3 Orange[Red,Yellow]
Alternatively, you could use recursive CTE
;with CTE(ID, Value, rec_count)
as (
select distinct ID, Value, 1 as rec_count from #t1 inner join #t2 on charindex(Search, Value ) > 0
union all
select ID, Value = replace(Value, Search, ReplaceWith), rec_count +1
from CTE
inner join #t2 on charindex(Search, Value ) > 0
)
update #t1
set Value= replaced.Value
from #t1 t
inner join
( select distinct ID, Value
from CTE c
where rec_count > 1
and rec_count = (select max(rec_count) from CTE where ID = c.ID) ) replaced on replaced.ID = t.ID
Simply use following UPDATE by cross-joined select statement and enjoy it! ;)
UPDATE tFirst
SET Value = REPLACE(tFirst.Value, tSecond.Search, tSecond.Replace)
FROM
[First] tFirst
CROSS JOIN [Second] tSecond

Insert multiple rows into SQL Server

I have two tables
Product (product_id, productName)
ProductSerialNumber (ProductSerialNumber_id, product_id, serialNumber, status)
I have serial numbers 12345679000 to 123456790100 (quantity: 90) for product : MILK
Is there way to do this without using multiple inserts ie
$Sn = 12345679000;
while ($Sn <= 123456790100 )
{
INSERT INTO ProductSerialNumber VALUES(...,...,$Sn,...)
$Sn++;
}
You can do this:
WITH Temp
AS
(
SELECT n
FROM(VALUES(0), (1), (2), (3), (4), (5), (6), (7), (8), (9)) AS Temp(n)
), nums
AS
(
SELECT id = t1.n * 10 + t2.n + 1
FROM temp AS T1
CROSS JOIN temp AS t2
)
INSERT INTO ProductSerialNumber(serialnumber)
SELECT 12345679000 + id AS Serialnumber -- You can insert into other columns too
FROM nums;
SQL Fiddle Demo.
Note that: This syntax FROM(VALUES(0), (1), ..., (9)) AS Temp(n) is new to SQL Server-2008, for old versions you can use SELECT ... UNION ALL SELECT ... instead.
However, if possible, you can alter this table and make this column SerialNumber an IDENTITY(12345679000, 1) and it will be auto incremental.
Update 1
For SQL Server 2005, try this:
WITH Temp
AS
(
SELECT 1 AS id
UNION ALL
SELECT 2
UNION ALL
SELECT 3
UNION ALL
SELECT 4
), nums
AS
(
SELECT ROW_NUMBER() OVER(ORDER BY t1.id) AS id
FROM temp t1, temp t2, temp t3, temp t4
)
INSERT INTO ProductSerialNumber(serialnumber)
SELECT 12345679000 + id AS Serialnumber
FROM nums
WHERE id <= 100;
Updated SQL Fiddle Demo.
Update 2
*How does this query work? *
Firstly, I define a virtual table with only four values:
SELECT 1 AS id
UNION ALL
SELECT 2
UNION ALL
SELECT 3
UNION ALL
SELECT 4
I defined it inside a Common table expression(CTE), to reuse it later.
Then in the following CTE, I used:
FROM temp t1, temp t2, temp t3, temp t4
This will join the table temp four times with it self, therefore it will give you: 44 = 256 rows. Then I used the ranking function ROW_NUMBER() as a work a round to generate a sequence number of number from 1 to 265.
The last thing is the syntax of INSERT INTO ... SELECT ... to select the numbers <= 100 of the numbers that we already generated from the previous step and inserting them to the table.
Hope this makes sense.
here another method to insert multiple rows with procedure
CREATE PROCEDURE autoInsert
#SerialNumberStart bigint,
#SerialNumberEnd bigint,
#status int,
#productID int
AS
while #SerialNumberStart <= #SerialNumberEnd
begin
BEGIN TRAN
INSERT INTO ProductSerialNumber VALUES(#SerialNumberStart)
--print #SerialNumberStart
COMMIT TRAN;
set #SerialNumberStart=#SerialNumberStart+ 1
end