SQL - Find orders with certain item combo(from another table) - loops

I have these 3 tables - CodesetList, Rule, Order:
In the first row of the Rule table, if a client ordered any of the CODE_ID under CODESET_ID=12 and any of the CODE_ID under CODESET_ID = 7 on the same service date(DAYS_FROM_Service=0), I want to find the transaction in Order table. Ultimately, if a client orders a combination of two items(Rule table), we want to deny the order according to the rule table.
Sorry, I'm bad at explaining this.
Rule:
CodesetList(note that ABC0274 is under Codeset_ID 9 & 10 and this is expected):
Order:
-- Create CodsetList table
SELECT '7' as CODESET_ID, 'ABC0210' as CODE_ID INTO #CodesetList UNION ALL
SELECT '8','ABC0220' UNION ALL
SELECT '8','ABC0230' UNION ALL
SELECT '8','ABC0240' UNION ALL
SELECT '8','ABC0250' UNION ALL
SELECT '8','ABC0260' UNION ALL
SELECT '9','ABC0270' UNION ALL
SELECT '9','ABC0272' UNION ALL
SELECT '9','ABC0274' UNION ALL
SELECT '9','ABC0273' UNION ALL
SELECT '10','ABC0274' UNION ALL
SELECT '11','ABC0277' UNION ALL
SELECT '12','ABC0330' UNION ALL
SELECT '83','ABC5110' UNION ALL
SELECT '83','ABC5120' UNION ALL
SELECT '83','ABC5130' UNION ALL
SELECT '83','ABC5140'
-- Create Rule table
SELECT '12' as TARGET_CODESET_ID, '7' as DENIAL_CODESET_ID, '2' as DIRECTION_FROM_DOS, '0' as DAYS_FROM_Service INTO #Rule UNION ALL
SELECT '83','7','1','365' UNION ALL
SELECT '7','8','2','0' UNION ALL
SELECT '7','9','2','0'
-- Create Order table
SELECT 'C3340' as ClientID, CAST('2019-10-12' AS DATE) as Service_Date, 'ABC0210' as CODE_ID INTO #Order UNION ALL
SELECT 'C3340',CAST('2019-10-12' AS DATE),'ABC0220' UNION ALL
SELECT 'C3340',CAST('2018-10-23' AS DATE),'ABC0272' UNION ALL
SELECT 'C3340',CAST('2019-10-09' AS DATE),'ABC0220' UNION ALL
SELECT 'C7646',CAST('2019-11-07' AS DATE),'ABC5110' UNION ALL
SELECT 'C7646',CAST('2019-05-07' AS DATE),'ABC0210' UNION ALL
SELECT 'C5376',CAST('2018-12-02' AS DATE),'ABC8411' UNION ALL
SELECT 'C5376',CAST('2018-10-18' AS DATE),'ABC8411' UNION ALL
SELECT 'C5376',CAST('2018-08-06' AS DATE),'ABC8161' UNION ALL
SELECT 'C9873',CAST('2019-01-06' AS DATE),'ABC6517' UNION ALL
SELECT 'C9873',CAST('2019-01-28' AS DATE),'ABC7784' UNION ALL
SELECT 'C9873',CAST('2019-03-05' AS DATE),'ABC8110'
Here's my poor attempt.
It only addresses one of the rules (row 3 from Rule table: Target=7, Denial=8)
And it also ignores the Days_From_Service rule.
SELECT *
FROM (SELECT *
FROM #Order AS o
WHERE o.CODE_ID IN(SELECT CODE_ID FROM #CodesetList WHERE CODESET_ID=7)) AS T
INNER JOIN
(SELECT *
FROM #Order AS o
WHERE o.CODE_ID IN(SELECT CODE_ID FROM #CodesetList WHERE CODESET_ID=8)) AS D ON T.ClientID=D.ClientID AND T.Service_Date=D.Service_Date;

Related

Sqlite display table with informations about the databas

They are asking me to write a query that display the following table:
Select each table as a string
Select the number of attributes as an integer (count the number of attributes per table).
Select the number of rows using the COUNT(*) function
Use the compound-operator UNION ALL to bind these rows together.
How do I do that?
I knew that for getting the name of each table I have to use
SELECT name AS table_name
FROM sqlite_schema
WHERE type = 'table'
but the rest is a mistery to me.
Would you help me?
Thank you!!!
Here you can find the database
Just had the same question, and the answer boiled down to 'manually' (looking at the schema visualisation or the aforementioned query for inputs). >_<
SELECT 'Customers' AS table_name,
13 AS number_of_attribute,
COUNT(*) AS number_of_row
FROM Customers
UNION ALL
SELECT 'Products' AS table_name,
9 AS number_of_attribute,
COUNT(*) AS number_of_row
FROM Products
UNION ALL
SELECT 'ProductLines' AS table_name,
4 AS number_of_attribute,
COUNT(*) AS number_of_row
FROM ProductLines
UNION ALL
SELECT 'Orders' AS table_name,
7 AS number_of_attribute,
COUNT(*) AS number_of_row
FROM Orders
UNION ALL
SELECT 'OrderDetails' AS table_name,
5 AS number_of_attribute,
COUNT(*) AS number_of_row
FROM OrderDetails
UNION ALL
SELECT 'Payments' AS table_name,
4 AS number_of_attribute,
COUNT(*) AS number_of_row
FROM Payments
UNION ALL
SELECT 'Employees' AS table_name,
8 AS number_of_attribute,
COUNT(*) AS number_of_row
FROM Employees
UNION ALL
SELECT 'Offices' AS table_name,
9 AS number_of_attribute,
COUNT(*) AS number_of_row
FROM Offices;

T-SQL: Need Top N to always return N rows, even if null or blank

T-SQL: Need Top N to always return N rows, even if null or blank
Typically, the command
Select Top 5 * FROM ourTable
will return up to 5 rows, but less, depending whether the rows exist.
I want to ensure that it always returns 5 rows, (or in general N rows).
What is the syntax to achieve this?
The idea is to sort of generalize the LINQ concept of "FirstOrDefault" to "First_N_OrDefault", but using TSQL not LINQ.
Clearly, the 'extra' rows would have null or empty columns.
This is for Microsoft SQL Server 2014 using SSMS 14.0.17
I want to use the "TOP" syntax, if at all possible, therefore it is different than the possible duplicate. Also, as noted below, this is possibly something that could be solved at a different layer in the system, but it would be nice to have for TSQL as well.
select top (5) c1, c2, c3 from (
select top (5) c1, c2, c3, 0 as priority from ourTable
union all
select c1, c2, c3, 1 from (values (null, null, null), (null, null, null), (null, null, null), (null, null, null), (null, null, null)) v (c1, c2, c3)
) t
order by priority
You can use another dummy table with rows to generate empty rows of your table with a not matching JOIN. So you don't have to repeat the columns and rows in the UNION ALL part:
SELECT TOP 5 * FROM (
SELECT 0 AS isDummy, * FROM table_name
-- WHERE column_name = value
UNION ALL
SELECT 1 AS isDummy, t1.* FROM table_name t1
RIGHT JOIN INFORMATION_SCHEMA.COLUMNS ON t1.id = -1000 -- not valid condition so t1 columns are empty.
) t2
ORDER BY isDummy ASC
In this case the INFORMATION_SCHEMA.COLUMNS table is used to generate the additional rows. You can choose any other table with rows. You can use a TOP N value up to the count of rows in the right table (here: INFORMATION_SCHEMA.COLUMNS).
You can also generate a table with many rows (like on a calendar table):
SELECT TOP 5 * FROM (
SELECT 0 isDummy, * FROM table_name
-- WHERE column_name = value
UNION ALL
SELECT 1 isDummy, t1.* FROM table_name t1 RIGHT JOIN (
SELECT * FROM
(SELECT 0 t0 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) t0,
(SELECT 0 t1 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) t1,
(SELECT 0 t2 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) t2,
(SELECT 0 t3 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) t3,
(SELECT 0 t4 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) t4
) t2 ON t1.id = -1000 -- not valid condition so t1 columns are empty.
)x
ORDER BY isDummy ASC
You can use a limited tally table together with a gaplessly generated row-number like here:
The SELECT is always the same. The only thing changing is the amount of rows in the mockup-table:
DECLARE #TopCount INT=5;
--Case 1: More then 5 rows in the table
DECLARE #tbl TABLE(ID INT IDENTITY,SomeValue VARCHAR(100));
INSERT INTO #tbl VALUES
('Value1'),('Value2'),('Value3'),('Value4'),('Value5'),('Value6'),('Value7');
WITH Tally(Nmbr) AS(SELECT TOP(#TopCount) ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) FROM master..spt_values)
,NumberedRows AS(SELECT ROW_NUMBER() OVER(ORDER BY ID) AS GeneratedRowNumber, * FROM #tbl)
SELECT *
FROM NumberedRows nr
FULL OUTER JOIN Tally t ON nr.GeneratedRowNumber=t.Nmbr;
--Case 2: Less than 5 rows in the table
DELETE FROM #tbl WHERE ID BETWEEN 2 AND 5;
WITH Tally(Nmbr) AS(SELECT TOP(#TopCount) ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) FROM master..spt_values)
,NumberedRows AS(SELECT ROW_NUMBER() OVER(ORDER BY ID) AS GeneratedRowNumber, * FROM #tbl)
SELECT *
FROM NumberedRows nr
FULL OUTER JOIN Tally t ON nr.GeneratedRowNumber=t.Nmbr;
--Case 3: Exactly one row in the table
DELETE FROM #tbl WHERE ID <> 6;
WITH Tally(Nmbr) AS(SELECT TOP(#TopCount) ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) FROM master..spt_values)
,NumberedRows AS(SELECT ROW_NUMBER() OVER(ORDER BY ID) AS GeneratedRowNumber, * FROM #tbl)
SELECT *
FROM NumberedRows nr
FULL OUTER JOIN Tally t ON nr.GeneratedRowNumber=t.Nmbr;
--Case 4: Table is empty
DELETE FROM #tbl;
WITH Tally(Nmbr) AS(SELECT TOP(#TopCount) ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) FROM master..spt_values)
,NumberedRows AS(SELECT ROW_NUMBER() OVER(ORDER BY ID) AS GeneratedRowNumber, * FROM #tbl)
SELECT *
FROM NumberedRows nr
FULL OUTER JOIN Tally t ON nr.GeneratedRowNumber=t.Nmbr;
This will return all rows from the source, but at least the specified count.
If you want to limit the set to exactly 5 rows (e.g. in "Case 1"), you can use SELECT TOP(#TopCount) * and place an appropriate ORDER BY. This would return the specified row count in any case.

Converting Binary to INT in SQL Server

I have an obsolete 4GL application, which keeps IDs as binary without any encryption.
I have tried simple CAST AS INT, CAST (SUBSTRING(..,3,1000) AS INT) and so to no avail. Can one advise, please?
In the example below binary_source column is a sample data from SQL Server, correct_result one - the result, presented in 4GL application and in between - some of my attempts to convert.
WITH binary_to_convert AS
(
SELECT 0x1841506A00000000 AS binary_source,100005 AS correct_result
UNION ALL SELECT 0x1841506A00000000,100005
UNION ALL SELECT 0x1841506A00000000,100005
UNION ALL SELECT 0x1841506A00000000,100005
UNION ALL SELECT 0x1841506A00000000,100005
UNION ALL SELECT 0x1841706A00000000,100007
UNION ALL SELECT 0x1841706A00000000,100007
UNION ALL SELECT 0x1841906A00000000,100009
UNION ALL SELECT 0x1841906A00000000,100009
UNION ALL SELECT 0x1841706A00000000,100007
UNION ALL SELECT 0x1841706A00000000,100007
UNION ALL SELECT 0x1841706A00000000,100007
UNION ALL SELECT 0x1841706A00000000,100007
UNION ALL SELECT 0x1841706A00000000,100007)
SELECT b.binary_source,
cast(b.binary_source AS INT) AS INT_COLUMN,
CAST(SUBSTRING(b.binary_source,3,1000) AS VARCHAR(MAX)) AS VARCHAR_COLUMN,
CAST(SUBSTRING(b.binary_source,3,1000) AS INT) AS INT_CAST_COLUMN,
b.correct_result
FROM binary_to_convert b

Inserting multiple rows in SQL?

I have WindowTable with following data :
SELECT Id FROM WindowTable WHERE OwnerRef=12
Id
----
25000
25001
25003
25004
25005
25006
25007
25008
I want to Insert 3 row per each WindowTable Row in ActionTable
Like this :
Id WindowsRef ActionName ActionName2
-----------------------------------------------
1 25000 'Add' 'E'
2 25000 'DELETE' 'H'
3 25000 'UPDATE' 'B'
4 25001 'ADD' 'E'
5 25001 'DELETE' 'H'
6 25001 'Update' 'B'
. . .
. . .
ActionTable.Id is not identity column
Or:
insert into ActionTable (Id, WindowsRe, ActionName, ActionName2)
select
isnull((select max(at.Id) from ActionTable at), 0) +
row_number() over (order by w.Id, a.Action),
w.Id, a.Action, a.Action2
from WindowsTable w
cross join
(
select 'Add' as Action, 'E' as Action2
union all select 'Delete', 'H'
union all select 'Update', 'B'
) a
UPD: Fixed misstyping. Thanks #Hua_Trung for comment
UPD2: Added ActionTable.Id generation
UPD3: Added ActionName2
Something like:
insert into ActionTable(WindowsRef, ActionName)
select id WindowsRef, 'Add'
from WindowsTable
union all
select id WindowsRef, 'DELETE'
from WindowsTable
union all
select id WindowsRef, 'UPDATE'
from WindowsTable
(Assuming ActionTable.Id is an identity column or otherwise database generated.)
To generate id values as well
insert into ActionTable(id, WindowsRef, ActionName)
select
(isnull(select max(id) from ActionTable, 0)
+ row_number() over (order by x.WindowsRef, x.ActionName)
) id,
x.WindowsRef, x.ActionName
from (
select id WindowsRef, 'Add' ActionName
from WindowsTable
union all
select id WindowsRef, 'DELETE' ActionName
from WindowsTable
union all
select id WindowsRef, 'UPDATE' ActionName
from WindowsTable
) x
one more approach using apply.Using sample data from Praveen ND
select *,row_number() over (order by id) as id from #WindowTable
cross apply
(
values('add','E'),
('delete','h'),
('update','b')
)b(action,action2)
This will help you to create the script for inserting to ActionTable
DECLARE #WindowTable TABLE (ID INT)
INSERT INTO #WindowTable VALUES
(25001),
(25003),
(25004),
(25005),
(25006),
(25007),
(25008)
SELECT 'INSERT INTO ActionTable (WindowsRe,ActionName) VALUES ('+ CAST(ID AS NVARCHAR(MAX))+',ADD)' FROM #WindowTable
UNION
SELECT 'INSERT INTO ActionTable (WindowsRe,ActionName) VALUES ('+ CAST(ID AS NVARCHAR(MAX))+',UPDATE)' FROM #WindowTable
UNION
SELECT 'INSERT INTO ActionTable (WindowsRe,ActionName) VALUES ('+ CAST(ID AS NVARCHAR(MAX))+',DELETE)' FROM #WindowTable
Note : Considering ID in ActionTable as IDENTITY.
IF ID in ActionTable is not an IDENTITY :
Try to make use of below Query :
DECLARE #WindowTable TABLE (ID INT)
INSERT INTO #WindowTable VALUES
(25001),
(25003),
(25004),
(25005),
(25006),
(25007),
(25008)
DECLARE #id INT =1;
DECLARE #id1 INT = (SELECT COUNT(*) FROM #WindowTable)
DECLARE #id2 INT = (SELECT 2 *COUNT(*) FROM #WindowTable)
SELECT 'INSERT INTO ActionTable (ID,WindowsRe,ActionName,ActionName2) VALUES ('+CAST(ROW_NUMBER() OVER(ORDER BY #id) AS NVARCHAR(MAX))+','+ CAST(ID AS NVARCHAR(MAX))+',ADD,''E'')' FROM #WindowTable
UNION
SELECT 'INSERT INTO ActionTable (ID,WindowsRe,ActionName,ActionName2) VALUES ('+CAST(#id1+ ROW_NUMBER() OVER(ORDER BY #id1) AS NVARCHAR(MAX))+','+ CAST(ID AS NVARCHAR(MAX))+',UPDATE,''B'')' FROM #WindowTable
UNION
SELECT 'INSERT INTO ActionTable (ID,WindowsRe,ActionName,ActionName2) VALUES ('+CAST(#id2+ ROW_NUMBER() OVER(ORDER BY #id2) AS NVARCHAR(MAX))+','+ CAST(ID AS NVARCHAR(MAX))+',DELETE,''H'')' FROM #WindowTable
Hope this helps
Test Data
;WITH cte_TestData(WindowsRef) AS
(
SELECT 25000 UNION ALL
SELECT 25001 UNION ALL
SELECT 25003 UNION ALL
SELECT 25004 UNION ALL
SELECT 25005 UNION ALL
SELECT 25006 UNION ALL
SELECT 25007 UNION ALL
SELECT 25008
)
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS ID,
a.WindowsRef,
b.ActionName,
b.ActionName2
FROM cte_TestData a
CROSS JOIN (
SELECT 'Add' AS ActionName,'E' AS ActionName2 UNION ALL
SELECT 'DELETE','H' UNION ALL
SELECT 'UPDATE','B') b
ORDER BY a.WindowsRef
Against Actual Data
;WITH cte_TestData(WindowsRef) AS
(
SELECT Id
FROM WindowTable
WHERE OwnerRef=12
)
,cte_Action AS
(
SELECT 'Add' AS ActionName,'E' AS ActionName2 UNION ALL
SELECT 'DELETE','H' UNION ALL
SELECT 'UPDATE','B'
)
--INSERT INTO <DestinationTable>
/*
- Replace <DestinationTable> with Target Table Name
- If the destination table has data the the ID has to be incremented accordingly.
-- In that case define a variable, get MAX of that ID and add that to the below
auto generated ID to preserve the sequence.
--Better yet, use Identity column as your ID Column
*/
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS ID,
a.WindowsRef,
b.ActionName,
b.ActionName2
FROM cte_TestData a
CROSS JOIN cte_Action b
--WHERE NOT EXISTS (SELECT 1 FROM <DestinationTable> c WHERE c.WindowsRef = a.WindowsRef)
/*
Enable to ensure duplicate records are not inserted, Replace <DestinationTable> with Target Table Name
*/
ORDER BY a.WindowsRef

'ß' and 'ss' being merged in group by [duplicate]

If I execute the code below:
with temp as
(
select 'Test' as name
UNION ALL
select 'TEST'
UNION ALL
select 'test'
UNION ALL
select 'tester'
UNION ALL
select 'tester'
)
SELECT name, COUNT(name)
FROM temp
group by name
It returns the results:
TEST 3
tester 2
Is there a way to have the group by be case sensitive so that the results would be:
Test 1
TEST 1
test 1
tester 2
You need to cast the text as binary (or use a case-sensitive collation).
With temp as
(
select 'Test' as name
UNION ALL
select 'TEST'
UNION ALL
select 'test'
UNION ALL
select 'tester'
UNION ALL
select 'tester'
)
Select Name, COUNT(name)
From temp
Group By Name, Cast(name As varbinary(100))
Using a collation:
Select Name Collate SQL_Latin1_General_CP1_CS_AS, COUNT(name)
From temp
Group By Name Collate SQL_Latin1_General_CP1_CS_AS
You can use an case sensitive collation:
with temp as
(
select 'Test' COLLATE Latin1_General_CS_AS as name
UNION ALL
select 'TEST'
UNION ALL
select 'test'
UNION ALL
select 'tester'
UNION ALL
select 'tester'
)
SELECT name, COUNT(name)
FROM temp
group by name
Simply:
SELECT count(*), CAST(lastname as BINARY) AS lastname_cs
FROM names
GROUP BY lastname_cs;
In MySQL/MariaDB, if you don't want to use collations or casting to binary, just use:
SELECT MAX(name), COUNT(name)
FROM (
select 'Test' as name
UNION ALL
select 'TEST'
UNION ALL
select 'test'
UNION ALL
select 'test'
UNION ALL
select 'tester'
UNION ALL
select 'tester'
) as tmp
group by MD5(name)
This works on my case:
SELECT BINARY example FROM table GROUP BY BINARY example;

Resources